哈喽,大家好,我是了不起。
创新互联建站专注于网站建设|成都网站维护|优化|托管以及网络推广,积累了大量的网站设计与制作经验,为许多企业提供了网站定制设计服务,案例作品覆盖成都岗亭等行业。能根据企业所处的行业与销售的产品,结合品牌形象的塑造,量身建设品质网站。
今天我们继续看看AOP相关的知识,前面说到了Javassit,Spring AOP,通过该篇,让你对AOP有更完整的认识。
再看AOP,这是一种面向切面编程思想,相比面向对象编程,可以说是站在更改维度关注对象,我们知道,对象包含由属性和行为。 基于AOP,我们可以把一段代码插入到对象中形成新的对象,这是织入的过程,目的是将公共的内容写入到业务代码中,通过配置或简单的编码完成整个过程。 这样一来不用修改原有的业务代码,同时又能自由完成目标代码的增强,按照代码的设计思想,确实是降低业务与功能的耦合。
大部分框架都是为我们提供切面织入目标过程的封装。
通过该图可以看到AOP相关的实现主要包括ASM、Cglib、JDK Proxy、AspectJ、Javassit,这些实现主要都是对字节码直接操作,只不过对目标对象的增强可以发生在编译时、编译后或运行时。
关于AOP我们说的比较多的就是代理,这属于设计模式的一种,但是AOP真正做的不仅仅是对目标的代理,更多的是修改,像我们常用的代理工具Cglib、JDK Proxy,都是基于面向对象的特性,生成新的 目标对象,通过继承与代理模式来实现最终的增强效果。
在Java中,大部分情况下都是对方法的增强,比如Spring AOP,这样可以解决几乎所有的业务问题;当然切点不局限于类方法,还可以包括字段、方法、构造函数、静态初始值等,比如AspectJ,只不过需要特定的 编译器来实现。
下面我们看下剩下的几项实现AOP的技术,前面说到,Spring AOP主要基于Cglib、JDK Proxy,在运行时实现目标对象的代理。但是Spring中却引入了aspectj相关的依赖,但没有用到AspectJ编译器
JDK动态代理,主要是基于目标接口,通过ByteArrayOutputStream直接构建字节数组,最终生成代理接口的实现类,基于InvocationHandler实现代码的扩展与增强,通过反射来调用目标代码的调用。
public interface HelloService {
String hello(String name);
}
@Slf4j
public class HelloServiceImpl implements HelloService{
@Override
public String hello(String name) {
log.info("+++ 执行方法:hello");
return String.format("hello, %s", name);
}
}
public class JdkProxyFactory {
public staticT create(Class targetClass, InvocationHandler invocationHandler){
return (T) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class[]{targetClass}, invocationHandler);
}
@Slf4j
public static class LogInvocationHandler implements InvocationHandler{
private Object target;
public LogInvocationHandler(Object target) {
this.target = target;
}
/**
*
* @param proxy
* @param method
* @param args
*
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
log.info(">>> before");
Object result = method.invoke(target, args); // 执行被代理方法
log.info(">>> afterReturning : {}", result);
return result;
} catch (Throwable e) {
log.info(">>> afterThrowing : {}", e.getMessage());
throw e;
} finally {
log.info(">>> after");
}
}
}
}
public class JdkProxyTests {
@Test
public void testJdkProxy(){
HelloService helloService = JdkProxyFactory.create(HelloService.class, new JdkProxyFactory.LogInvocationHandler(new HelloServiceImpl()));
helloService.hello("JDK Proxy");
}
}
Cglib基于目标类来实现代理,已目标类为参考基于ASM直接操作字节码,构造目标对象的子类行,基于MethodInterceptor接口实现目标代码的增强,通过父类调用来执行原目标代码,因此在执行效率上会高于JDK动态代理。
cglib
cglib
3.3.0
@Slf4j
public class HiService {
public String hi(String name){
log.info("+++ 执行方法:hi");
return String.format("hi, %s", name);
}
}
public class CglibFactory{
/**
*
* @param targetClass
* @param methodInterceptor
* @return
* @param
*/
public staticT create(Class targetClass, MethodInterceptor methodInterceptor){
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(targetClass);
enhancer.setCallback(methodInterceptor);
return (T) enhancer.create();
}
@Slf4j
public static class LogMethodInterceptor implements MethodInterceptor {
/**
*
* @param target 目标对象
* @param method 目标方法
* @param args 参数
* @param methodProxy 代理方法,注意执行方式 methodProxy.invokeSuper
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object target, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
try {
log.info(">>> before");
Object result = methodProxy.invokeSuper(target, args); // 执行被代理方法
log.info(">>> afterReturning : {}", result);
return result;
} catch (Throwable e) {
log.info(">>> afterThrowing : {}", e.getMessage());
throw e;
} finally {
log.info(">>> after");
}
}
}
}
public class CglibTests {
/**
*
*/
@Test
public void testCglib(){
HiService hiService = CglibFactory.create(HiService.class, new CglibFactory.LogMethodInterceptor());
hiService.hi("Cglib");
}
}
AspectJ是一个功能强大的面向切面编程框架,是对Java面向对象的扩展,支持编译时、编译后、加载时为目标对象(不仅仅是类方法)织入代理。
切面织入时机:
我们可以通过AspectJ编译器或者maven插件aspectj-maven-plugin来实现。
aspectj
java -jar aspectj-1.9.6.jar 配置环境变量PATH与系统变量CLASSPATH
通过下面的命令可实现编译时织入的效果:
# ajc [Options] [file... | @file... | -argfile file...]
ajc -1.8 -sourceroots .\src\main\java\ -cp %CLASS_PATH% -outjar main.jar
通过ajc编译后并打包成main.jar,即是编译时实现了目标对象的代理,通过反编译工具可以查看到编译后的目标对象已经被修改。
编译时织入(Compile-Time Weaving)
目标对象:
public class CTWObject {
public void run() {
System.out.println("-- Compile-Time Weaving --");
}
}
Aspect:
public aspect CTWAspect {
pointcut pc():
execution(* com.sucl.blog.aspectj.target.CTWObject.*());
before(): pc(){
System.out.println(" >> before CTW << ");
}
void around(): pc(){
System.out.println(" >> around before CTW << ");
proceed();
System.out.println(" >> around before CTW << ");
}
after(): pc(){
System.out.println(" >> after CTW << ");
}
}
org.codehaus.mojo
aspectj-maven-plugin
1.14.0
1.8
1.8
true
true
ignore
UTF-8
compile
test-compile
public class AspectJCTWTests {
@Test
public void call() {
CTWObject CTWObject = new CTWObject();
CTWObject.run();
}
}
编译后织入(Post-Compile Weaving)
编写测试的目标对象,并打包成jar文件
public class PCWObject {
public void run() {
System.out.println("-- Post-Compile Weaving --");
}
}
com.sucls.blog
PCW-target
${project.version}
org.codehaus.mojo
aspectj-maven-plugin
1.11
1.8
1.8
UTF-8
com.sucls.blog
PCW-target
compile
test-compile
mvn clean compile
public class AspectJPCWTests {
@Test
public void call(){
PCWObject pcwObject = new PCWObject();
pcwObject.run();
}
}
运行时织入(Load-Time Weaving)
-javaagent:${project.basedir}/lib/aspectjweaver-1.9.7.jar
或者配置maven-surefire-plugin插件
org.apache.maven.plugins
maven-surefire-plugin
2.10
-javaagent:${project.basedir}/lib/aspectjweaver-1.9.7.jar
true
always
/src/main/resources/META-INF/aop.xml
public class AspectJLTWTests {
@Test
public void call(){
LTWObject LTWObject = new LTWObject();
LTWObject.run();
}
}
不管是javassit,还是jdk proxy或者cglib来实现AOP,都是通过对字节码的修改,只不过对字节码操作方式不一样。通过上面的例子我们可以认识到各种AOP框架的使用方式。在究其原理时, 能够能够知道这些工具到底为我们做了什么。
当前标题:关于AOP,大概就到这了吧!
网站URL:http://www.shufengxianlan.com/qtweb/news44/128644.html
网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联