Spring-AOP
AOP 面向切面编程,是spring中最核心的技术之一
你可以将业务模块中横向上公共的部分抽取出来,利用spring-aop在你需要的时候为你自动注入,而不需要修改你的源代码,开发中就可以更加专注于业务的开发。
动态代理和静态代理
静态代理:静态代理需要你手动实现代理类对象。通过和被代理类实现相同的接口的方式,在代理类中调用被代理类的方法,在代理类中编写需要增强的代码。
动态代理:由机器自动的为你生成代理类对象。你只需要定义被代理类对象,被代理类对象实现的接口。
JDK 动态代理
spring的aop实现切面编程的底层技术是使用了动态代理的机制,有两种动态代理的方式,一种是JDK的动态代理,另一种的cglib动态代理,spring默认使用的是JDK动态代理。
注意:JDK动态代理,需要被代理类必须实现接口。
JDK 动态代理代理的是接口
JDK 动态代理的实现是通过Proxy类的newProxyInstance()方法创建一个代理对象,这个方法需要三个参数
-
参1:被代理类的类加载器 classLoader
-
参2:被代理类实现的接口(必须实现接口,可以实现多个接口):class<?>[]
-
参3:代理类处理程序,反射机制自动帮你调用被代理类的需要被代理的方法,在调用方法的前、后可以编写你需要注入的代码
//对代理过程进行了封装,使之变得通用
public class ProxyInvocationHandler implements InvocationHandler {
private Object target;
public void setTarget(Object target) {
this.target = target;
}
public Object getProxyInstance() {
//通过Proxy 的newProxyInstance()方法得到一个代理对象
return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
}
//invoke 方法,调用被代理类的方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//proxy 被代理的对象
//method 被代理的方法
//args 方法的参数
System.out.println("代理了" + proxy.getClass().getName() + "的" + method.getName() + "方法");
Object result = method.invoke(target, args);
return result;
}
}
-
接口
public interface ILaoBan { void eat(); }
-
实现类
public class LaoBan implements ILaoBan { @Override public void eat() { System.out.println("吃饭"); } }
-
test
public class LaoBanTest { @Test public void test01 () { ILaoBan laoBan = new LaoBan(); ProxyInvocationHandler pih = new ProxyInvocationHandler(); pih.setTarget(laoBan); ILaoBan instance = (ILaoBan) pih.getProxyInstance(); instance.eat(); } }
Spring 实现AOP
AOP的一些术语概念
- Aspect:切面,一个类,需要注入的代码
- pointcut:切入点,在被代理类中被增强的方法
- joinPoint:连接点,被代理类的所有方法
- weave:织入,就是将代理类中需要增强的方法放入到目标类中去执行的过程
将原方法与其他类的方法一起调用 - advice:通知,将代理对象中的方法应用到目标类的过程中产生的结果。
xml配置实现
-
1、导入依赖的jar包
<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.9.RELEASE</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.7.2</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.2.9.RELEASE</version> </dependency> </dependencies>
-
2、定义一个接口
public interface IUser { public void work(); }
-
目标类实现接口
public class UserImpl implements IUser { @Override public void work() { System.out.println("写代码"); } }
-
定义一个切面类
public class MyAdivce { public void bolgAdvice() { System.out.println("写完代码写个写个博客!"); } }
-
在applicationContext.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: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.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"> <!--配置包扫描--> <context:component-scan base-package="com.jsu"> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan> <bean id="user" class="com.jsu.UserImpl"/> <bean id="advice" class="com.jsu.MyAdivce"/> <aop:config> <!--配置一个切点,被代理类中具体被增强的方法--> <aop:pointcut id="work" expression="execution(public void com.jsu.UserImpl.work())"/> <!--配置一个切面 --> <aop:aspect ref="advice"> <!--将切面中的哪一个方法 注入到被代理类的方法中--> <aop:after method="bolgAdvice" pointcut-ref="work"/> </aop:aspect> </aop:config> </beans>
-
test
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext.xml") public class AppTest { @Autowired private IUser user; @Test public void shouldAnswerWithTrue() { user.work(); } }
-
输出结果
注解实现
注解实现,可以省去一些xml配置。
注解实现就是在切面类上加上@Aspect注解,在配置文件中开启注解支持自动代理
-
applicationContext.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: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.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"> <!--配置包扫描--> <context:component-scan base-package="com.jsu"> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan> <bean id="user" class="com.jsu.UserImpl"/> <bean id="advice" class="com.jsu.MyAdivce"/> <aop:aspectj-autoproxy/> <!--<aop:config> <!–配置一个切点,被代理类中具体被增强的方法–> <aop:pointcut id="work" expression="execution(public void com.jsu.UserImpl.work())"/> <!–配置一个切面 –> <aop:aspect ref="advice"> <!–将切面中的哪一个方法 注入到被代理类的方法中–> <aop:after method="bolgAdvice" pointcut-ref="work"/> </aop:aspect> </aop:config>--> </beans>
-
切面类
@Aspect public class MyAdivce { //前置通知 @Before("execution(public void com.jsu.UserImpl.work())") public void before() { System.out.println("写代码前喝个饮料"); } //后置通知 @After("execution(public void com.jsu.UserImpl.work())") public void after() { System.out.println("写完代码写个写个博客!"); } @Around("execution(public void com.jsu.UserImpl.work()))") public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { System.out.println("环绕前"); //开始执行切点的方法 Object proceed = proceedingJoinPoint.proceed(); System.out.println("环绕后"); } }
-
test
expression 表达式
- (1)什么是切面表达式
execution([修饰符] 返回值类型 包.类.方法(参数列表) );
- (2)切面表达式有什么用?
符合表达式的方法,会被增强
使用* 表过任意的内容
使用… 可以表示包与子包下面的类
使用…可以写在方法(…)表示任意参数