配置文件和注解混合使用
- 创建对象操作使用配置文件方式实现
- 注入属性的操作使用注解方式实现
例子:
创建 BookService, OrderDao, BookDao 三个类;想要在 BookService 类中获得另外两个类的对象。
通过配置文件,进行类的实例的注入:我们在这里新建另一个名为applicationContext2.xml
;同样由于我们和注解一同使用,我们也要引入并开启注解扫描。
<?xml version="1.0" encoding="UTF-8"?>
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 开启注解扫描
1. 到包所包含的类中进行扫描类,方法,属性上是否有注解存在
-->
<context:component-scan base-package="club.teenshare"></context:component-scan>
<!-- 配置对象 -->
<bean id="bookService" class="club.teenshare.service.BookService"></bean>
<bean id="bookDao" class="club.teenshare.service.BookDao"></bean>
<bean id="orderDao" class="club.teenshare.service.OrderDao"></bean>
</beans>
然后,我们在 BookService.java
类中进行注解定义:
public class BookService {
//得到 BookDao 以及 OrderDao 的对象
@Resource(name="bookDao")
private BookDao bookDao;
@Resource(name="orderDao")
private OrderDao orderDao;
public void add(){
System.out.println("service........");
bookDao.book();
orderDao.buy();
}
}
注意:这里 @Resoure(name="?")
中的 name 的值需要同配置文件中的 id
名称一致
另外两个 java 类文件则无需在修改,他们已经在 xml 文件中完成了配置。然后我们就可以通过 ApplicationContext 进行使用了。
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext2.xml");
BookService bookService = (BookService)context.getBean("bookService");
bookService.add();
}
AOP 概念
AOP (Aspect Oriented Programming): 面向切面(方面)编程,拓展功能不修改源代码实现。
AOP 采用横向抽取机制,取代了传统纵向继承体系的重复性代码。
AOP 原理
最开始的处理方式和纵向抽取机制
aop 横向抽取机制:有接口的情况
aop 横向抽取机制:无接口的情况
Spring 的 AOP 的底层实现
Spring的 AOP 的底层用到了两种代理机制:
JDK
的动态代理:针对实现了接口的类产生代理。Cglib
的动态代理:针对没有实现接口的类产生代理,应用的是底层的字节码增强的技术,生成当前类的子类对象。
AOP 操作术语
- Joinpoint(连接点):所谓连接点是指那些被拦截到的点。在Spring中,这些点指的是方法,因为Spring只支持方法类型的连接点。
-
⭐Pointcut(切入点):所谓切入点是指我们要对哪些 Joinpoint 进行拦截的定义。
- ⭐Advice(通知/增强):所谓通知是指拦截到 Joinpoint 之后所要做的事情就是通知。通知分为前置通知、后置通知、异常通知、最终通知和环绕通知(切面要完成的功能)。
- ⭐Aspect(切面):是切入点和通知的结合。
- Target(目标对象):代理的目标对象(要增强的类)
- Weaving(织入):是指把增强应用到目标对象来创建新的代理对象的过程。Spring采用动态代理织入,而 AspectJ 采用编译期织入和类装载期织入。
- Proxy(代理):一个类被 AOP 织入增强后,就产生一个结果代理类。
- Introduction(引介):引介是一种特殊的通知在不修改类代码的前提下,Introduction 可以在运行期为类动态地添加一些方法或 Field。
为了便于理解,我们假设有一个类:
public class User {
public void add() {
}
public void update() {
}
public void delete() {
}
}
- Joinpoint 连接点:在 User 类里面有 3 个方法,这 3 个方法都可以被增强,类里面的哪些方法可以被增强,这些方法就可被成为连接点。
-
Pointcut 切入点:在一个类中可以有很多的方法被增强,在实际操作中,如若只增强了类里面的 add 方法,则实际增强的方法被称为切入点。
-
Advice 增强/通知:比如增强 User 类里面的 add 方法,在 add 方法中添加了日志功能,这个日志功能就称为增强。
通知类型:
- 前置通知:在增强的方法执行之前进行操作。
- 后置通知:在增强的方法执行之后进行操作。
- 异常通知:程序出现异常之后执行的通知。
- 最终通知:增强了两个方法,执行第一个方法,执行第二个方法,在第二个方法执行之后进行操作。
也可理解为后置通知后面执行的通知或者无论目标方法是否出现异常,最终通知都会执行。
- 环绕通知:在增强的方法执行之前和执行之后进行操作。
-
Aspect 切面:把增强应用到切入点的过程。即把具体增强的逻辑用到具体的方法上面的过程。
-
Introduction 引介:引介是一种特殊的通知,在不修改类代码的前提下,引介在运行期间为类动态的添加一些方法或 字段
-
Target 目标对象:增强的方法所在的类,即要增强的类。
-
Weaving 织入:是指把增强应用到目标对象的过程。即把 advice 应用到 target 的过程。
基于 AspectJ 开发
@AspectJ 的简介
AspectJ 是一个面向切面的框架,它扩展了 Java 语言。AspectJ 定义了 AOP 语法所以它有一个专门的编译器用来生成遵守 Java 字节编码规范的 Class 文件。AspectJ 是一个基于 Java 语言的 AOP 框架。Spring2.0 以后新增了对 AspectJ 切点表达式的支持。@AspectJ 是 JDK5 新增的功能,通过 JDK5 注解技术,允许直接在 Bean 类中定义切面。
新版本Spring框架,建议使用 AspectJ 方式来开发 AOP,使用 AspectJ 需要导入Spring AOP 和 AspectJ 相关的 Jar 包。
从上面的阐述中,我们应认识到 AspectJ 并不是 Spring 框架的一部分,而是一个单独的面向切面的框架,只不过它经常和 Spring 框架一起使用进行 AOP 的操作而已。
使用 AspectJ 方式来开发 AOP 共有两种方式:
- 基于 AspectJ 的 xml 配置文件 的方式
- 基于 AspectJ 的 注解 的方式
使用表达式配置切入点
- 切入点: 实际增强的方法
-
常用表达式:
execution(<访问修饰符>?<返回类型><方法名>(<参数>)<异常>)
具体例子:(注意在访问修饰符后有一个空格)
- 匹配所有类的 public 方法:
execution(public *.*(..))
- 匹配所有类里面的所有的方法:
execution(* *.*(..))
execution(* cn.itcast.dao..*(..))
,..*
表示包、子孙包下所有类。- 匹配指定类所有方法:
execution(* cn.itcast.service.UserService.*(..))
- 匹配实现特定接口的所有类的方法:
execution(* cn.itcast.dao.GenericDAO+.*(..))
- 匹配所有 save 开头的方法:
execution(* save*(..))
- 匹配指定包下所有类的方法:
execution(* cn.itcast.dao.*(..))
,但不包含子包
- 匹配所有类的 public 方法:
Spring 使用 AspectJ 进行 AOP 开发:XML 的方式
第一步,引入相应的 Jar 包
使用@AspectJ
的话,除了导入最基本的 Jar 包外,使用 AspectJ 还需要导入Spring AOP
和AspectJ
相关的 Jar 包。
Spring 的传统 AOP 的开发的包:
- spring-aop-4.2.4.RELEASE.jar
- aopalliance-1.0.jar
AspectJ 的开发包:
- aspectjweaver-1.8.7.jar
- spring-aspects-4.2.4.RELEASE.jar
第二步,创建 spring 核心配置文件,导入 aop 约束
<?xml version="1.0" encoding="UTF-8"?>
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
">
第三步:编写目标类以及增强类(增强类用于增强目标类)
目标类 Book.java
// 目标类,会被增强类增强
package club.teenshare.aop;
public class Book {
public void add(){
System.out.println("add..........");
}
}
增强类 MyBook.java
// 增强类,用于增强目标类
// 增强类,用于增强目标类
package club.teenshare.aop;
import org.aspectj.lang.ProceedingJoinPoint;
public class MyBook {
public void before1(){
System.out.println("前置增强.........");
}
public void after1(){
System.out.println("后置增强.........");
}
// 环绕通知
public void arround1(ProceedingJoinPoint joinPoint) throws Throwable{
System.out.println("方法之前执行...........环绕");
// 让被增强的方法执行
joinPoint.proceed();
System.out.println("方法之后执行...........环绕");
}
}
第四步,配置 applicationContext.xml 文件指定增强类与目标类
<!-- 1 配置对象 -->
<bean id="book" class="club.teenshare.aop.Book"></bean>
<bean id="myBook" class="club.teenshare.aop.MyBook"></bean>
<!-- 2 配置 aop 的操作 -->
<aop:config>
<!-- 2.1 配置切入点 -->
<aop:pointcut expression="execution(* club.teenshare.aop.Book.*(..))" id="pointcut1"/>
<!-- 2.2 配置切入面
将增强用到方法上的过程
-->
<aop:aspect ref="myBook">
<!-- 配置增强的类型
method:有不同的增强方式,前置增强等等
pointcut-ref: 指定 pointcut 的 id
-->
<aop:before method="before1" pointcut-ref="pointcut1"/>
<!-- 后置通知 -->
<aop:after-returning method="after1" pointcut-ref="pointcut1"/>
<!-- 环绕类型 -->
<aop:around method="arround1" pointcut-ref="pointcut1"/>
</aop:aspect>
</aop:config>
测试类代码:
@Test
public void testAop(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext3.xml");
Book book = (Book)context.getBean("book");
book.add();
}
Spring 使用 AspectJ 进行 AOP 开发:注解方式
第一步:创建对象
<!-- 创建对象 -->
<bean id="book" class="club.teenshare.aop.Book"></bean>
<bean id="myBook" class="club.teenshare.aop.MyBook"></bean>
第二步:在 spring 的核心配置文件中,开启 aop 操作
<!-- 开启 aop 操作 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
第三步:在增强类的上边可以使用注解来实现操作
@Aspect
来标注增强类;在增强的方法上加上增强类型以及 execution 表达式
增强类 MyBook.java 代码:
@Aspect // 表明是增强类
public class MyBook {
// 前置通知
// *:方法的访问修饰符,也可写为 execution(public void club.teenshare.aop.Book.*(..)),但一般都不会用
@Before("execution(* club.teenshare.aop.Book.*(..))")
public void say(){
System.out.println("MyBook>..........");
}
}
另外附上目标类 Book.java
以及测试类代码:
Book.java:
public class Book {
public void name(){
System.out.println("Book's Name:........");
}
}
TestExample.java
public class TestExample {
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Book book = (Book) context.getBean("book");
book.name();
}
}