面向切面编程AOP
AOP(Aspect Orient Programming),面向切面编程
切面:公共的,通用的,重复的功能称为切面,就是将切面提取出来,单独开发,在需要调用的地方通过动态代理的方式来调用
java1.8接口组成更新,接口新增默认方法,关键字default,不需要继承接口的类Override我们的方法,默认是空实现
静态代理:就是Agent来代理切面和业务但是需要经常去改变,需要动态代理来帮助构建代理类
动态代理的代码:
//Service接口
package org.example.poxy3;
public interface Service {
void buy();
}
//implements Service接口的
package org.example.poxy3;
public class BookServiceImpl implements Service {
@Override
public void buy() {
System.out.println("书店服务买了几本书");
}
}
Aop切面层
//Aop接口
package org.example.poxy3;
public interface Aop {
default void before(){}
default void after(){
System.out.println("事务关闭");
}
default void exception(){
System.out.println("事务回滚");
}
}
//使用LoginAop接口的类
package org.example.poxy3;
public class LoginAop implements Aop {
@Override
public void before() {
System.out.println("日志开启");
}
}
package org.example.poxy3;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyFactory {
public static Object getProxy(Service target,Aop aop){
return Proxy.newProxyInstance(
//类加载器
target.getClass().getClassLoader(),
//目标对象实现的所有接口
target.getClass().getInterfaces(),
//类实现的所有方法
new InvocationHandler() {
@Override
public Object invoke(
//生成的代理对象
Object proxy,
//代理的方法
Method method,
//方法的参数
Object[] args) throws Throwable {
Object obj = null;
try {
aop.before();
obj=method.invoke(target, args);
aop.after();
} catch (IllegalAccessException e) {
aop.exception();
}
return obj;
}
}
);
}
}
Spring原生aop的实现:
专业术语:
AspectJ框架:
Aspect框架使用案例:
业务接口和业务类实现
//业务接口
package org.example;
public interface SomeService {
String someError(String name,int age);
void doSome();
}
//业务类
package org.example;
import org.springframework.stereotype.Service;
@Service
public class SomeServiceImpl implements SomeService{
@Override
public String someError(String name, int age) {
System.out.println("业务开启");
return "null";
}
@Override
public void doSome() {
System.out.println("你好啊");
}
}
//切面实现
package org.example;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class MyAspect {
// public String someError(String name, int age)
@Before(value = "execution(public * org.example.SomeService.*(..))")
public void before(){
System.out.println("前置切面开启");
}
}
applicationcontext动态代理和CGLib代理之间的转换可以根据proxy-target-class的设置来相互转化,转换之后就可以用不用代理的原始接口来承接了,可以使用业务实现类来承接
applicationcontext的实现:
<?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">
<bean class="org.example.SomeServiceImpl" name="service"></bean>
<bean class="org.example.MyAspect" name="myAspect"></bean>
<!-- proxy-target-class设置为true表示的是将动态proxy代理换成CGLib动态代理-->
<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
</beans>
JoinPoint jp参数详解
jp.getSignature()得到目标方法的签名
jp.getArgs() 得到目标方法的参数
Crtl+H可以看出来类的继承关系
后置通知:
applicationcontext:
<?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="org.example.s01"></context:component-scan>
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
对象类:
package org.example.s01;
public class Student {
private int age;
private String name;
public Student() {
}
public Student(int age, String name) {
this.age = age;
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
}
//业务接口
package org.example.s01;
public interface SomeService {
String dosome(int age);
Student insert();
}
//业务接口实现类
package org.example.s01;
import org.springframework.stereotype.Service;
@Service
public class ServiceImpl implements SomeService {
@Override
public String dosome(int age) {
return "null";
}
@Override
public Student insert() {
return new Student(11,"zhangsan");
}
}
//切面类
package org.example.s01;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class MyAspectj {
@AfterReturning(value = "execution(public * org.example.s01.ServiceImpl.*(..))",returning ="obj"
)
public void after(Object obj){
if(obj instanceof String){
System.out.println(obj);
System.out.println("后置切面开启");}
else if (obj instanceof Student){
Student obj0 = (Student)obj;
obj0.setAge(2222);
System.out.println("后置切面的对象是"+obj);
System.out.println("后置切面开启");
}
}
}
总结:后置接口方法的参数obj不可以修改基本类型的value,但是可以修改注入类型的value。
除了excution还需要,returning这个参数,但是returning这个参数里面必须和下面的Object 的参数一样,下面是obj所以这里也必须得是obj