文章目录
欢迎来到我的个人博客
一、AOP:
是对OOP编程方式的一种补充。翻译过来为“面向切面编程”。
可以理解为一个拦截器框架,但是这个拦截器会非常武断,如果它拦截一个类,那么它就会拦截这个类中的所有方法。如对一个目标列的代理,增强了目标类的所有方法。
两个解决办法:
1.传统做法:
在添加增强时,根据方法名去判断,是否添加增强,但是这样就得一直去维护这个增强类。
2.面向切面:
将增强类和拦截条件组合在一起,然后将这个切面配置到 ProxyFactory 中,从而生成代理。
二、AOP 和 切面的关系
1.类比于 OOP 和 对象,AOP 和 切面就是这样的一种关系。
2.也可以将 切面 看成是 AOP 的一个工具。
三、几个概念
切面(Advisor):是AOP中的一个术语,表示从业务逻辑中分离出来的横切逻辑,比如性能监控,日志记录,权限控制等。
这些功能都可以从核心的业务逻辑中抽离出去。可以解决代码耦合问题,职责更加单一。封装了增强和切点。
增强(Advice):增强代码的功能的类,横切到代码中。
目标:目标方法(JDK代理)或目标类(CGLIB代理)
代理:JDK代理,CGLIB代理。或是通过 ProxyFactory 类生产。
切点:通过一个条件来匹配要拦截的类,这个条件称为切点。如拦截所有带 Controller 注解的类。增强的条件。
连接点:作为增强方法的入参,可以获取到目标方法的信息。
四、概括为一张图
SpringAOP 的底层代理的两种方式:
1.jdk动态代理方式(只对实现接口的类进行代理)
1.创建接口
package com.imooc.Demo1;
public interface usersImp {
public void sava();
public void update();
public void delect();
public void add();
}
2.创建实现类
package com.imooc.Demo1;
public class Users implements usersImp {
public void sava() {
System.out.println("查询");
}
public void update() {
System.out.println("修改");
}
public void delect() {
System.out.println("删除");
}
public void add() {
System.out.println("添加");
}
}
3.创建JDK动态代理
package com.imooc.Demo1;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/*Jdk的动态代理*/
public class jdkproxy implements InvocationHandler {
private usersImp usersImp;
jdkproxy(usersImp usersImp) {
this.usersImp = usersImp;
}
public Object createProxy() {
// users.getClass().getClassLoader() 类的加载器
// users.getClass().getInterfaces() 实现的接口
// this InvocationHandler接口 实例
Object proxy = Proxy.newProxyInstance(usersImp.getClass().getClassLoader(), usersImp.getClass().getInterfaces(), this);
return proxy;
}
//进行代理增强
// Object proxy 代理类对象
// Method method 代理对象的方法参数
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ("sava".equals(method.getName())) {
System.out.println("sava 代理增强");
return method.invoke(usersImp, args);
}else {
return method.invoke(usersImp, args);
}
}
}
测试类
package com.imooc.Demo1;
import org.junit.Test;
public class Test2 {
@Test
public void test() {
usersImp usersImp = new Users();
// 对users对象进行代理
usersImp proxy = (usersImp) new jdkproxy(usersImp).createProxy();
proxy.add();
proxy.delect();
proxy.sava();
proxy.update();
}
}
CGLIB 代理(字节码增强技术)
1.创建实体类
package com.imooc.Demo2;
public class Users {
public void sava() {
System.out.println("查询");
}
public void update() {
System.out.println("修改");
}
public void delect() {
System.out.println("删除");
}
public void add() {
System.out.println("添加");
}
}
2.创建cglib 代理类
package com.imooc.Demo2;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class Cjlib implements MethodInterceptor {
private Users users;
Cjlib(Users users) {
this.users = users;
}
public Object createCglib() {
// 1.创建核心类
Enhancer enhancer = new Enhancer();
// 设置父类
enhancer.setSuperclass(users.getClass());
//设置反转
enhancer.setCallback(this);
// 产生代理
Object proxy = enhancer.create();
return proxy;
}
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
if ("sava".equals(method.getName())) {
System.out.println("sava 代理增强");
return methodProxy.invokeSuper(proxy, args);
}
return methodProxy.invokeSuper(proxy, args);
}
}
3.进行测试
import org.junit.Test;
public class test {
@Test
public void Test2() {
Users users = new Users();
// 创建代理类
Users proxy = (Users) new Cjlib(users).createCglib();
proxy.add();
proxy.delect();
proxy.sava();
proxy.update();
}
}
五、增强
1.Weaving(织入):对方法进行增强
(1)前置增强(BeforeAdvice):在目标方法前调用。
(2)后置增强(AfterAdvice):在目标方法后调用。
(3)环绕增强(AroundAdvice):将 Before 和 After ,甚至抛出增强和返回增强合到一起。
(4)返回增强(AfterReturningAdvice):在方法返回结果后执行,该增强可以接收到目标方法返回结果。
(5)抛出增强(AfterThrowingAdvice):在目标方法抛出对应的类型后执行,可以接收到对应的异常信息。
2.Introduction(引入):对类进行增强
(1)引入增强(DeclareParentsAdvice):想让程序在运行的时候动态去实现某个接口,需要引入增强。
Spring 一般切面代码实现(不带切入点)
引入Spring开发所需jar包
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.imooc.Springaop</groupId>
<artifactId>springaop</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.1.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.0.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.0.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.0.8.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.0.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.1.1.RELEASE</version>
</dependency>
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
</dependencies>
</project>
2.创建接口
package com.imooc.Demo4;
public interface StudentDao {
public void add();
public void delect();
public void update();
public void sava();
}
3.创建实现类
package com.imooc.Demo4;
public class Studaoimp implements StudentDao {
public void add() {
System.out.println("添加");
}
public void delect() {
System.out.println("删除");
}
public void update() {
System.out.println("修改");
}
public void sava() {
System.out.println("查询");
}
}
4.创建增强类
package com.imooc.Demo4;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
public class MybeforAdvice implements MethodBeforeAdvice {
// 前置通知
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println("前置增强");
}
}
5.创建spring配置文件并进行配置
<?xml version="1.0" encoding="UTF-8"?>
<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">
<!--配置目标类-->
<bean id="Studentdao" class="com.imooc.Demo4.Studaoimp"/>
<!--前置通知类-->
<bean id="mybeforAdvice" class="com.imooc.Demo4.MybeforAdvice"></bean>
<!--是否对类进行代理,当实现类没有接口时 设置为true时 使用CGlib 代理-->
<!-- <property name="proxyTargetClass" value="true"></property> -->
<!--Springaop 产生代理-->
<bean id="SpringDaopoxry" class="org.springframework.aop.framework.ProxyFactoryBean">
<!--配置目标类-->
<property name="target" ref="Studentdao"/>
<!--配置目标类实现的接口-->
<property name="proxyInterfaces" value="com.imooc.Demo4.StudentDao"/>
<!--配置要是增强的拦截器名称-->
<property name="interceptorNames" value="mybeforAdvice"/>
</bean>
</beans>
6.运行日志
前置增强
添加
前置增强
查询
前置增强
删除
前置增强
修改
SpringAOP 切点切面代码实现(带切入点)
- 使用一般切面将目标类所有方法进行拦截增强不够灵活,常用带切点的PointcutAdvisor 实现类!
1.创建实体类
package com.imooc.Demo5;
public class Studao {
public void add() {
System.out.println("添加");
}
public void delect() {
System.out.println("删除");
}
public void update() {
System.out.println("修改");
}
public void sava() {
System.out.println("查询");
}
}
2.创建通知类
package com.imooc.Demo5;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
public class MyAroundAdvice implements MethodInterceptor {
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
//在这前就是环绕前通知
// 执行目标方法
System.out.println("环绕前增强");
Object proceed = methodInvocation.proceed();
//环绕后通知
System.out.println("环绕后增强");
return proceed;
}
}
3.配置spring 配置文件
<?xml version="1.0" encoding="UTF-8"?>
<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">
<!--配置目标类-->
<bean id="Studao" class="com.imooc.Demo5.Studao"></bean>
<!--环绕通知配置-->
<bean id="myAroundAdvice" class="com.imooc.Demo5.MyAroundAdvice"></bean>
<!--一般切面是用通知为切面,带切入点的要先配置待遇切入点的切面-->
<bean id="myadvice" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<!--配置切入点 正则表达式-->
<property name="pattern" value=".*save.*"/>
<!--使用哪种配置通知-->
<property name="advice" ref="myAroundAdvice"/>
</bean>
<bean id="test2" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="Studao"/>
<property name="proxyTargetClass" value="true"/>
<property name="interceptorNames" value="myadvice"/>
</bean>
</beans>
4.进行测试
package com.imooc.Demo5;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import javax.annotation.Resource;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:Springaop2.xml")
public class test {
@Resource(name = "test2")
private Studao studao;
@Test
public void test(){
studao.add();
studao.delect();
studao.update();
studao.sava();
}
}