一.动态代理
1.动态代理的特点
*字节码随用随创建,随用随加载。
*它与静态代理的区别也在于此。因为静态代理是字节码一上来就创建好,并完成加载。
*装饰者模式就是静态代理的一种体现。
2.两种动态代理方式:
*基于接口的动态代理:
由jdk的Proxy类提供
要求被代理类至少实现一个接口
*基于子类的动态代理:
由第三方提供, CGLib,如果报 asmxxxx 异常,需要导入 asm.jar。
要求被代理类不是final
3.为什么使用动态代理:
*在不改变源码的基础上,增强被代理对象的方法。
二.AOP
1.作用
面向切面编程,spring代替程序猿,不必再编写代理模式,而是交给spring管理动态代理,来解耦方法间的依赖。
开发者只需要关注增强方法的部分。
2.术语
*Joinpoint(连接点): 有可能需要进行增强的方法
*Pointcut(切入点): 在所有有可能被增强的方法中,确实需要增强的方法
*Advice(通知/增强):增强方法的具体实现
通知的类型:前置通知,后置通知,异常通知,最终通知,环绕通知。
*Aspect(切面): 切入点和通知的整合
*Weaving(织入): 是指把增强应用到目标对象来创建新的代理对象的过程。 spring 采用动态代理织入,而 AspectJ 采用编译期织入和类装载期织入
3.步骤
*编写核心业务代码
*抽取重复代码,形成通知
*在xml配置文件中说明切入点与通知的织入,
*在运行阶段,交给spring管理,将通知织入到切入点当中,生成动态代理对象。
4.注意
spring根据切入点所在类是否为接口来判断,使用哪种代理方式。
三.基于xml的AOP实现
1.jar包
2.xml中导入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:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
</beans>
3.配置aop
<!-- 配置 service --> <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl"> <property name="accountDao" ref="accountDao"></property> </bean> <!-- 配置 dao --> <bean id="accountDao" class="com.itheima.dao.impl.AccountDaoImpl"> property name="dbAssit" ref="dbAssit"></property> </bean> <!-- 配置数据库操作对象 --> <bean id="dbAssit" class="com.itheima.dbassit.DBAssit"> <property name="dataSource" ref="dataSource"></property> <!-- 指定 connection 和线程绑定 --> <property name="useCurrentConnection" value="true"></property> </bean> <!-- 配置数据源 --> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="com.mysql.jdbc.Driver"></property> <property name="jdbcUrl" value="jdbc:mysql:///spring_day02"></property> <property name="user" value="root"></property> <property name="password" value="1234"></property> </bean>
4.将公共方法抽取为通知类
public class TransactionManager { };
5.把通知类用 bean 标签配置起来
<!-- 配置通知 --> <bean id="txManager" class="com.itheima.utils.TransactionManager"> <property name="dbAssit" ref="dbAssit"></property> </bean>
6.使用 aop:config 声明 aop 配置
aop:aspect:
作用: 用于配置切面。 属性: id:给切面提供一个唯一标识。 ref:引用配置好的通知类 bean 的 id。
<aop:aspect id="txAdvice" ref="txManager"> <!--配置通知的类型要写在此处--> </aop:aspect>
7.:使用 aop:pointcut 配置切入点表达式
aop:pointcut: 作用: 用于配置切入点表达式。就是指定对哪些类的哪些方法进行增强。 属性: expression:用于定义切入点表达式。 id:用于给切入点表达式提供一个唯一标识
<aop:pointcut expression="execution( public void com.itheima.service.impl.AccountServiceImpl.transfer( java.lang.String, java.lang.String, java.lang.Float) )" id="pt1"/>
8.使用 aop:xxx 配置对应的通知类型
aop:before
作用: 用于配置前置通知。指定增强的方法在切入点方法之前执行 属性: method:用于指定通知类中的增强方法名称 ponitcut-ref:用于指定切入点的表达式的引用 poinitcut:用于指定切入点表达式
<aop:before method="beginTransaction" pointcut-ref="pt1"/>
*aop:before
*aop:after-returning
*aop:after-throwing
*aop:after
*aop:around环绕通知:用于实现自定义通知顺序
9.
<aop:config> <aop:pointcut expression="execution(* com.itheima.service.impl.*.*(..))" id="pt1"/> <aop:aspect id="txAdvice" ref="txManager"> <!-- 配置环绕通知 --> <aop:around method="transactionAround" pointcut-ref="pt1"/> </aop:aspect> </aop:config>
9.通常情况下,我们都是对业务层的方法进行增强,所以切入点表达式都是切到业务层实现类。 execution(* com.itheima.service.impl.*.*(..))
四.基于注解的AOP实现
1.jar包
2.在配置文件中导入 context 的名称空间
<?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:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
">
</beans>
3.:把资源使用注解配置
4.在配置文件中指定 spring 要扫描的包
<!-- 告知 spring,在创建容器时要扫描的包 --> <context:component-scan base-package="com.itheima"></context:component-scan>
5.把通知类也使用注解配置
6.在通知类上使用@Aspect 注解声明为切面
@Component("txManager") @Aspect //表明当前类是一个切面类 public class TransactionManager { 定义一个 DBAssit @Autowired private DBAssit dbAssit; }
7.在增强的方法上使用注解配置通知
@Before
作用: 把当前方法看成是前置通知。 属性: value:用于指定切入点表达式,还可以指定切入点表达式的引用。
@Before("execution(* com.itheima.service.impl.*.*(..))") public void beginTransaction(){ try{dbAssit.getCurrentConnection().setAutoCommit(false); }catch(SQLException e) {e.printStackTrace();} }
*@Before
*@AfterReturning
* @AfterThrowing
*@After
*@Around
8.在 spring 配置文件中开启 spring 对注解 AOP 的支持
<!-- 开启 spring 对注解 AOP 的支持 --> <aop:aspectj-autoproxy/>
注:不使用xml的方式
@Configuration
@ComponentScan(basePackages="com.itheima")
@EnableAspectJAutoProxy
public class SpringConfiguration { }
9.@Pointcut和@Around:
*@Around的value:用于指定切入点表达式,还可以指定切入点表达式的引用。
@Pointcut("execution(* com.itheima.service.impl.*.*(..))") private void pt1() {};
@Around("pt1()")//注意:千万别忘了写括号