AOP:面向切面编程,它是一个编程的思想。核心旨在8个字:横向重复,纵向抽取。
即AOP技术能够将多个方法中相同而又与对应业务无关的代码给抽取到一个独立的类中统一实现。又或者对一个增强后的方法中将增强部分的实现统一抽取到一个独立的类来统一实现。
通俗一点来说AOP的思想就是把这些多个业务共同会使用到但又与具体对应业务逻辑无关的功能统一整合到一个类上来动态的为我们代理执行。也就是动态代理。最终达到维护性(只用修改一处)和阅读性的提升(业务实现部分中和具体业务无关的代码少了)。
具体实现的技术有filter,事务,加锁,权限管理等。
例如filter:
动态代理就是AOP思想的核心实现,JAVA中提供了一个proxy类来供我们实现动态代理
JAVA自带的proxy是接口代理。目标对象必须实现接口才能被代理
demo:
接下来完成这么一个例子,模拟操作数据库,其中操作时要使用事务。我们将开启和关闭事务的操作交给代理对象来完成
步骤:
1,材料准备–>接口,目标对象,被代理方法,可以获取代理对象的类
//接口
public interface DataBase {
void save();
}
//目标对象
public class DataBaseImpl implements DataBase{
@Override
//被代理方法
public void save() {
System.out.println("保存");
}
}
//获取代理对象的类。
public class ProxyDataBaseFactory implements InvocationHandler{
private DataBase dataBase;
public ProxyDataBaseFactory(DataBase dataBase) {
this.dataBase = dataBase;
}
public DataBase getDataBaseProxy(){
//获取代理对象,使用Proxy.newProxyInstance(classLoader,Interfaces[],InvocationHandler实现类)
//这边自身实现了InvocationHandler,所以第三个参数可以传this
return (DataBase)Proxy.newProxyInstance(DataBase.class.getClassLoader()
, DataBaseImpl.class.getInterfaces(),
this);
}
@Override
//代理对象执行的方法。在目标方法执行前开启事务,执行后关闭事务
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("事务开启");
Object result = method.invoke(dataBase, args);
System.out.println("事务关闭");
return result;
}
}
然后执行以下方法
public static void main(String[] args) {
ProxyDataBaseFactory proxyDataBaseFactory = new ProxyDataBaseFactory(new DataBaseImpl());
//生成代理对象
DataBase dataBaseProxy = proxyDataBaseFactory.getDataBaseProxy();
//执行保存
dataBaseProxy.save();
}
结果如下:
事务开启
保存
事务关闭
这样就一个简单的具有AOP思想的功能就实现了。目标方法中save只需执行它的保存工作。而与业务无关的事务的开启和关闭交由代理对象来完成.
到这一个具有AOP思想的动态代理实现就介绍完了。
JAVA提供的代理是接口代理。为了使任何一个对象都能被代理,于是引入了cglib代理。
SpringAOP将这两个代理结合。当使用SpringAOP时,如果目标对象没有实现接口就使用cglib代理否则使用动态代理。当然可以手动配置使其强制使用cglib代理。
cglib代理简介:cglib代理是继承代理,它可以使任何对象被代理。但要注意的是目标对象不能用final修饰
在玩SpringAOP之前要先了解以下7个AOP的专业术语
AOP专业术语:
JoinPoint(连接点):表示可以被代理的方法
PointCut(切入点):真正被代理的方法
Advice(通知):被代理的那部分功能的代码实现
Target(目标对象):被代理对象
awave(织入):Advice切入到JoinPoint的过程
proxy(代理):代理对象,即awave到JoinPoint形成的代理对象
aspect(切面):PointCut+Advice组成切面
SpringAOP实现步骤
1,首先要导包和在spring配置文件中写好命名空间
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"
如果使用idea的话就可以省去命名空间的引入。idea会自动引入
2,准备好材料
目标对象,被代理方法,代理对象,通知。
(1)目标对象和可以被代理的方法
/**
* 目标对象
*/
public class TargetObject {
//这两个方法都被称为连接点,即可以被代理的方法。
public void save(){
System.out.println("保存");
}
public void select(){
System.out.println("查询");
}
}
(2)通知
/**
* 通知,即被代理的那部分代码
**/
public class AdviceObject{
//前置通知,切入点执行前该执行的代码
public void before(){
System.out.println("前置通知");
}
//后置通知,切入点执行后该执行的代码,这个后置通知无论是否有异常都会执行
public void after(){
System.out.println("后置通知(无论是否异常都执行)");
}
//后置通知,切入点执行后该执行的代码,这个后置通知在代码出异常后不执行
public void afterReturn(){
System.out.println("后置通知(有异常不执行)");
}
//环绕通知,切入点执行前后都会执行的通知。
//传入一个对象ProceedingJoinPoint 并使用proceed()方法来控制切入点前后该执行什么
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("环绕通知前");
Object result = pjp.proceed();
System.out.println("环绕通知后");
return result;
}
//切入点执行出异常后会执行的代码
public void exception(){
System.out.println("异常通知");
}
}
3,配置application.xml
(1)首先要配置目标对象和通知对象这两个bean
(2)然后使用
配置aop
其中底下有两个标签
1,切点
切点有两个属性,
①id:和bean的id一样用于配置切面的时候引用
②expression:表达式,即配置需要被代理的方法
格式: execution([修饰符] 方法返回值 包.类.方法(参数列表类型))
比如 execution(* aop.datademo.*.*(..))
表示aop.datademo包下的所有类的所有方法都会被代理
常用的写法还有这些
表达式 | 描述 |
---|---|
execution(* aop.*service.*.*(..)) |
aop下以service结尾的包下的所有类的所有方法 |
execution(* aop.*service.*impl.*(..)) |
aop下以service结尾的包下的所有以impl结尾的类的所有方法 |
具体的xml配置如下
最后执行以下方法
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");
TargetObject targetObject = applicationContext.getBean("targetObject", TargetObject.class);
targetObject.save();
}
这样SpringAOP的配置和基础使用就介绍完了。在这个过程中,目标对象只完成属于它自己的保存方法。至于在它之前和之后要做什么就由SpringAOP来代理完成了