为什么学?
因为AOP就是基于代理模式中的动态代理来的---增加了代码的扩展性 使代码之间解耦
A、抽象的类或者接口 ---完成一件怎样的事情
B、被代理对象---事情操作具体内容
C、代理对象----帮助我们完成事情的同是可以增加其他的功能
房屋租赁
A、抽象的类或者接口--租房子
B、被代理对象---房东
C、代理对象---中介
1.定义抽象类或接口
2.被代理对象 实现接口
3.代理对象 实现接口
4.调用代理对象,实现功能
5.实现结果
接口
/**
* 定于需要完成的事情 -- 租房
*/
public interface LetRoom {
public void tenement();
}
被代理对象
/**
* 房东
*/
public class Landlord implements LetRoom{
@Override
public void tenement() {
System.out.println("--我要出租江景房5000元--");
}
}
代理对象
/**
* 在这个类中书写动态产生代理对象的方法
*/
public class MyInv implements InvocationHandler {
private LetRoom lr;
public void setLr(LetRoom lr) {
this.lr = lr;
}
/**
* 执行该方法就会产生动态代理对象
* @return
*/
public Object getProxy(){
/**
* 参数1 保证类型是ClassLoader
* 参数2 接口的Class数组
* 参数3 参数类型是InvocationHandler即可
* 运行后会产生中介
*/
Object o = Proxy.newProxyInstance(MyInv.class.getClassLoader(), new Class[]{LetRoom.class}, this);
return o;
}
/**
* 这个方法类似于书写中介添加的方法
* @param proxy 代理对象
* @param method 调用方法
* @param args 参数 null
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("-- 推荐费用50元 --");
//使用反射调用方法 指定房东的方法租房
Object invoke = method.invoke(lr, args);
System.out.println("-- 管理费500元 --");
return invoke;
}
}
测试类
package com.lin.proxy2;
public class Test {
public static void main(String[] args) {
MyInv myInv = new MyInv();
//设置具体的房东 设置被代理对象
myInv.setLr(new Landlord());
//产生中介代理对象
LetRoom proxy = (LetRoom) myInv.getProxy();
proxy.tenement();
}
}
使用cglib动态代理需要引入两个jar包cglib.jar, asm.jar
产生的代理对象和真实对象之间的关系: 父子类型关系.
代理对象是真实对象的子对象.
导包
被代理对象
/**
* 房东-被代理对象
*/
public class Landlord {
public void tenement() {
System.out.println("--我要出租江景房5000元--");
}
}
设置动态代理
package com.lin.proxy3;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class Methodinv implements MethodInterceptor {
//产生代理类对象
public Object getProxy(){
Enhancer en = new Enhancer();
//设置被代理对象 - 类似于接口
en.setSuperclass(Landlord.class);
//回调
en.setCallback(this);
//使整个设置生效
Object o = en.create();
//动态代理的中介
return o;
}
/**
*
* @param o 被代理对象
* @param method 被代理对象的方法
* @param objects 参数
* @param methodProxy 代理对象中方法
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("-- 推荐费用50元 --");
//调用正真房东的租房
Object o1 = methodProxy.invokeSuper(o, objects);
System.out.println("-- 管理费500元 --");
return o1;
}
}
测试类
package com.lin.proxy3;
public class Test {
public static void main(String[] args) {
Methodinv methodinv = new Methodinv();
Landlord proxy = (Landlord) methodinv.getProxy();
proxy.tenement();
}
}
通过学习之前的内容,我们知道了IOC的作用是帮助我们创建对象,而接下来学习的AOP就是提升代码的扩展性并且对代码进行解耦
Aop:Aspect Oriented Programming
中文含义:面向切面编程
没有AOP时, 代码的执行流程:
使用AOP后, 代码的执行流程
A、Schema base
B、AspectJ
我们使用传统方式进行项目开发的时候,我们书写好的功能模块后期的扩展比较的麻烦
使用AOP面向切面进行功能扩展
A、确定切点---需要扩展的方法
B、增加通知---
C、织入成切面
具体操作
创建需要扩展的原始类
package com.lin.pojo;
public class User {
public void a(){
System.out.println("a");
}
public void b(){
System.out.println("b");
}
public void c(){
System.out.println("c");
}
}
创建拓展类实现MethodBeforeAdvice 实现前置通知方法
配置切面applicationContext.xml
注意切点写法 * 空格 方法的全限定路径
* com.lin.pojo.User.a(..)
完整代码
<!--创建前置通知对象-->
<bean id="beforeAdvice" class="com.lin.advice.BeforeAdvice"></bean>
<!--织入切面-->
<aop:config>
<!--配置切点-->
<aop:pointcut id="pt1" expression="execution(* com.lin.pojo.User.a())"/>
<!--配置通知-->
<aop:advisor advice-ref="beforeAdvice" pointcut-ref="pt1"></aop:advisor>
</aop:config>
测试类
package com.lin.test;
import com.lin.pojo.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String[] args) {
//解析xml
ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
User user = (User) app.getBean("user");
user.a();
}
}
后置通知仅实现接口与前置接口不同(AfterReturningAdvice),其余写法基本一致
环绕通知仅实现接口与前置接口不同(MethodInterceptor),其余写法基本与前置一致
异常通知仅实现接口与前置接口不同(ThrowsAdvice),其余写法基本与前置一致
在使用的schema Baes 实现的方式,发现了每一个通知都需要实现对应的接口,每一个接口中就是一个方法,这样的书写方式比较的麻烦的,我们想所有的方法都在一个类中书写就比较方便了。
解决方式
Aspect J方式实现
我们发现 Aspect J的方式虽然可以把所有的通知都结合到一起,书写方便,但是获得切点中的参数和切点所在的类的时候比较的繁琐
所以需要在使用的时候考虑清楚
实现方式
需要扩展的原始类
package com.lin.pojo;
public class User {
public void a(String a){
int b = 5/0;
System.out.println("a");
}
public void b(){
System.out.println("b");
}
public void c(){
System.out.println("c");
}
}
创建定义拓展
package com.lin.advice;
import org.aspectj.lang.ProceedingJoinPoint;
/**
* @author Lin
*/
public class AspectJAdvice {
/**
* 前置通知方法
*/
public void beforeAd(){
System.out.println("前置通知");
}
/**
* 后置通知方法
*/
public void afterAd(){
System.out.println("后置通知");
}
/**
* 环绕通知方法
*/
public Object aroundAd(ProceedingJoinPoint point) throws Throwable {
System.out.println("环绕通知-前");
//放行
Object proceed = point.proceed();
System.out.println("环绕通知-后");
return proceed;
}
/**
* 异常通知方法
*/
public void throwAd(){
System.out.println("异常通知");
}
}
配置applicationContext.xml
<!--配置通知类的对象-->
<bean id="aps" class="com.lin.advice.AspectJAdvice"></bean>
<!--配置AspectJAdvice-->
<aop:config>
<aop:aspect ref="aps">
<aop:pointcut id="pt" expression="execution(* com.lin.pojo.User.b(..))"/>
<!--配置前置通知-->
<aop:before method="beforeAd" pointcut-ref="pt"></aop:before>
<!--配置后置通知 无论切点是否有异常,都会执行 但after-returning只适用于没有异常才会执行-->
<aop:after method="afterAd" pointcut-ref="pt"></aop:after>
<!--配置环绕通知-->
<aop:around method="aroundAd" pointcut-ref="pt"></aop:around>
<!--配置异常通知-->
<aop:after-throwing method="throwAd" pointcut-ref="pt"></aop:after-throwing>
</aop:aspect>
</aop:config>
A、schema base :如果我们需要使用切点中的参数或者切点所在的类对象的时候
B、aspect J:就是简单的给切点增加通知的时候使用这个方式比较简单
网站声明:如果转载,请联系本站管理员。否则一切后果自行承担。
添加我为好友,拉您入交流群!
请使用微信扫一扫!