动态代理
public class ProxyUtil {
private MathImpl mathImpl;//目标对象
public Object getProxy(){
//获取当前类的类加载器,用来加载代理对象所属类
ClassLoader loader = this.getClass().getClassLoader();
//获取目标对象实现的所有接口的class,代理类会和目标对象实现相同的接口,最终通过代理对象实现功能
Class[] interfaces = mathImpl.getClass().getInterfaces();
return Proxy.newProxyInstance(loader, interfaces, new InvocationHandler() {
//代理对象实现功能的方式
@Override
//动态代理:暂不需设置(proxy) 处理的方法(method) 处理的参数(args)
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{
MyLogger.before(method.getName(), Arrays.toString(args));
Object result = method.invoke(mathImpl, args);//动态代理对象实现功能
MyLogger.after(method.getName(), result);
return result;
}
});
}
public ProxyUtil(MathImpl mathImpl) {
super();
this.mathImpl = mathImpl;
}
}
public class MyLogger {
public static void before(String methodName,String args){
System.out.println("method:"+methodName+",arguments:"+args);
}
public static void after(String methodName,Object result){
System.out.println("method:"+methodName+",result:"+result);
}
public static void throwing(){
System.out.println("有异常");
}
}
public class Test {
public static void main(String[] args) {
ProxyUtil proxy = new ProxyUtil(new MathImpl());
MathI math = (MathI)proxy.getProxy();//生成代理对象
int i = math.div(4, 1);
System.out.println(i);
}
}
return Proxy.newProxyInstance(loader, interfaces, new InvocationHandler() {
//代理对象实现功能的方式
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
MyLogger.before(method.getName(), Arrays.toString(args));
Object result = method.invoke(mathImpl, args);//动态代理对象实现功能
MyLogger.after(method.getName(), result);
return result;
} catch (Exception e) {
// TODO: handle exception
MyLogger.throwing();
e.printStackTrace();
}finally{
System.out.println("哪都有我");
}
return null;
}
});
//通过try catch的方式来体会日志通知所使用的位置及其效果
纵向继承机制:当要使用类中的方法时,只能通过创建对象来调用方法
横向抽取机制:不用创建对象,而是通过代理的方式来直接使用类中的方法
AOP中的专业术语
先得有切入点(比如什么类的什么方法) 才能有连接点(哪个方法的哪个位置)
编写一个目标对象,然后在目标对象中抽取公共功能放入一个新类,这个新类称为切面,里面的功能方法称为通知,在目标对象的方法的周围使用切面的通知,这个使用通知的方法的位置称为切入点,目标对象使用的方法和位置称为连接点
前置通知案例学习
1.在spring配置中开启自动代理
<!-- 开启aspectJ的自动代理功能 -->
<aop:aspectj-autoproxy /> <!-- 简略版本 -->
<!-- 原本格式:<aop:aspectj-autoproxy> </aop:aspectj-autoproxy> -->
2.添加横切关注点(即将公共功能部分另开一个类) 添加注解@Aspect 为通知设置前置注解@Before
@Aspect//标注当前类为切面
public class MyloggerAspect {
//@Before:将方法指定为前置通知
//必须设置value,其值为切入点表达式
@Before(value = "execution(public int com.atguigu.spring.aop.MathImpl.add(int,int))")
public void beforeMethod(){
System.out.println("方法使用前");
}
}
3.需要将此时的切面(即MyloggerAspect)当做组件加入spring的配置文件.xml 设置扫描
<context:component-scan base-package="com.atguigu.spring.aop"></context:component-scan>
在切面注解上添加组件注解@Component 在目标对象MathImpl上也添加组件注解 表示2个类称为被spring管理的bean标签
4.在测试类中连接spring的配置文件
public class Test {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("aop.xml");
MathI math = ac.getBean("mathImpl", MathI.class);
System.out.println(math.sub(1, 1));
}
}
可对前置通知进行添加 是其显示更多信息 采用JoinPoint 是其不局限于某一功能块 变为通用
public void beforeMethod(JoinPoint joinPoint){
Object[] args = joinPoint.getArgs();//获取方法的参数
String methodName = joinPoint.getSignature().getName();//获取方法名
System.out.println("method:"+methodName+",arguments:"+Arrays.toString(args));
}
切入点表达式也可进行省略书写
@Before(value = "execution(* com.atguigu.spring.aop.*.*(..))")
//第一个*省略权限修饰符 第二个*省略类名 第三个*省略方法名 ..省略形参数据
在包目录下添加其他组件管理时,发现也可使用前置通知 采用的是静态代理 存在继承的关系
@Component
public class TestHandler {
public void test(){
System.out.println("测试切入点表达式");
}
}
public class Test {
public static void main(String[] args) {
TestHandler testHandler = ac.getBean("testHandler", TestHandler.class);
testHandler.test();
}
}