Spring AOP编程的半自动和全自动编写代理
Spring编写代理:半自动
- 让spring 创建代理对象,从spring容器中手动的获取代理对象。注意:这里从Spring中获取到的是代理对象,而不是目标类对象
AOP联盟通知类型
- AOP联盟为通知Advice定义了org.aopalliance.aop.Advice
- Spring按照通知Advice在目标类方法的连接点位置,可以分为5类
- 前置通知 org.springframework.aop.MethodBeforeAdvice
- 在目标方法执行前实施增强
- 后置通知 org.springframework.aop.AfterReturningAdvice
- 在目标方法执行后实施增强
- 环绕通知 org.aopalliance.intercept.MethodInterceptor(功能最强,可以实现其他类型的通知)
- 在目标方法执行前后实施增强
- 异常抛出通知 org.springframework.aop.ThrowsAdvice
- 在方法抛出异常后实施增强
- 引介通知 org.springframework.aop.IntroductionInterceptor
- 在目标类中添加一些新的方法和属性
- 前置通知 org.springframework.aop.MethodBeforeAdvice
环绕通知,必须手动执行目标方法
try{
//前置通知
//执行目标方法
//后置通知
} catch(){
//抛出异常通知
}
环绕通知
我们重点来实现环绕通知,因为它的功能最强大
- 编写目标类
首先我们需要编写目标类
public interface UserService {
public void addUser();
public void updateUser();
public void deleteUser();
}
public class UserServiceImpl implements UserService {
@Override
public void addUser() {
System.out.println("addUser()");
}
@Override
public void updateUser() {
System.out.println("updateUser()");
}
@Override
public void deleteUser() {
System.out.println("deleteUser()");
}
}
然后我们需要添加配置
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
">
<!-- 1 创建目标类 -->
<bean id="userServiceId" class="com.cc.study.aop_proxy.UserServiceImpl"></bean>
</beans>
然后进行测试看看
@Test
public void demo01(){
String xmlPath = "spring_aop_proxy.xml";
AbstractApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
UserService userService = (UserService) applicationContext.getBean("userServiceId");
userService.addUser();
userService.updateUser();
userService.deleteUser();
}
- 编写切面类
用于存放通知(也就是存放增强的代码)
/**
* 切面类中确定通知,需要实现不同接口,接口就是规范,从而就确定方法名称。
* * 采用“环绕通知” MethodInterceptor
*/
public class MyAspect implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
System.out.println("before");
//手动执行目标方法
Object obj = mi.proceed();
System.out.println("after");
return obj;
}
}
配置文件中添加切面类配置
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
">
<!-- 1 创建目标类 -->
<bean id="userServiceId" class="com.cc.study.aop_proxy.UserServiceImpl"></bean>
<!-- 2 创建切面类 -->
<bean id="myAspectId" class="com.cc.study.aop_proxy.MyAspect"></bean>
</beans>
- 创建代理类
之前我们用JDK动态代理还有CGLIB都是使用创建一个工厂类来返回代理。
现在我们把创建一个工厂类来返回代理过程交给spring
所以我们只需要添加一下Spring的配置,但是我们在Spring配置中配置这一个工厂类ProxyFactoryBean。所以这也就是为什么叫做半自动。
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
">
<!-- 1 创建目标类 -->
<bean id="userServiceId" class="com.cc.study.aop_proxy.UserServiceImpl"></bean>
<!-- 2 创建切面类 -->
<bean id="myAspectId" class="com.cc.study.aop_proxy.MyAspect"></bean>
<!-- 3 创建代理类
* 使用工厂bean FactoryBean ,底层调用 getObject() 返回特殊bean
* ProxyFactoryBean 用于创建代理工厂bean,生成特殊代理对象
interfaces : 确定接口们
通过<array>可以设置多个值
只有一个值时,value=""
target : 确定目标类
interceptorNames : 通知 切面类的名称,类型String[],如果设置一个值 value=""
optimize :强制使用cglib
<property name="optimize" value="true"></property>
底层机制
如果目标类有接口,采用jdk动态代理
如果没有接口,采用cglib 字节码增强
如果声明 optimize = true ,无论是否有接口,都采用cglib
-->
<bean id="proxyServiceId" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="interfaces" value="com.cc.study.aop_proxy.UserService"></property>
<property name="target" ref="userServiceId"></property>
<property name="interceptorNames" value="myAspectId"></property>
</bean>
</beans>
测试类
@Test
public void demo02(){
String xmlPath = "spring_aop_proxy.xml";
AbstractApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
UserService userService = (UserService) applicationContext.getBean("proxyServiceId");
userService.addUser();
userService.updateUser();
userService.deleteUser();
}
这里我们需要拿到代理类,然后调用它的方法。
Spring编写代理:全自动
- 从spring容器获得目标类,与半自动不同的是,这里从Spring获取到的是目标类。如果配置AOP,spring将自动生成代理。
我们首先要做的就是添加pom的依赖,添加spring aop的依赖还有aspectjweaver的依赖(用于确定目标类,aspectj 切入点表达式)
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.2.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.3</version>
</dependency>
然后我们要做的就是配置需要导入Spring AOP的命名空间
然后我们还是需编写目标类还有切面类,都跟半自动的一样,这里就不写了,配置上也是需要加上的。不同的点在于最后创建代理对象的时候,是交给Spring帮我们自动生成代理,所以我们主要就是需要在配置中加入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: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">
<!-- 1 创建目标类 -->
<bean id="userServiceId" class="com.cc.study.aop_proxy.UserServiceImpl"></bean>
<!-- 2 创建切面类(通知) -->
<bean id="myAspectId" class="com.cc.study.aop_proxy.MyAspect"></bean>
<!-- 3 aop编程
3.1 导入命名空间
3.2 使用 <aop:config>进行配置
proxy-target-class="true" 声明时使用cglib代理
<aop:pointcut> 切入点 ,从目标对象获得具体方法
<aop:advisor> 特殊的切面,只有一个通知 和 一个切入点
advice-ref 通知引用
pointcut-ref 切入点引用
3.3 切入点表达式
execution(* com.itheima.c_spring_aop.*.*(..))
选择方法 返回值任意 包 类名任意 方法名任意 参数任意
-->
<aop:config proxy-target-class="true">
<aop:pointcut expression="execution(* com.cc.study.aop_proxy.*.*(..))" id="myPointCut"/>
<aop:advisor advice-ref="myAspectId" pointcut-ref="myPointCut"/>
</aop:config>
</beans>
然后就是测试
@Test
public void demo03(){
String xmlPath = "spring_aop_proxy2.xml";
AbstractApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
UserService userService = (UserService) applicationContext.getBean("userServiceId");
userService.addUser();
userService.updateUser();
userService.deleteUser();
}
可以看到我们这里直接获取的是目标类