父类Bean:
@MyAnnotation(“father”)
public class Father {
public void hello() {
System.out.println(“father.hello()”);
}
public void hello2() {
System.out.println(“father.hello2()”);
}
}
子类Bean:
@MyAnnotation(“son”)
public class Son extends Father {
@Override
public void hello() {
System.out.println(“son.hello()”);
}
}
配置类:
@Configuration
@EnableAspectJAutoProxy(exposeProxy = true)
public class Config {
@Bean
public Father father() {
return new Father();
}
@Bean
public Son son() {
return new Son();
}
}
测试类:
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class,
MyAspect.class);
Father father = context.getBean(“father”, Father.class);
father.hello();
father.hello2();
Son son = context.getBean(Son.class);
son.hello();
son.hello2();
}
}
我们定义了一个@Before
通知,方法参数有point, myAnnotation
,方法里输出了myAnnotation.value
的值
下面是输出结果:
before, myAnnotation.value : father
father.hello()
before, myAnnotation.value : father
father.hello2()
before, myAnnotation.value : son
son.hello()
before, myAnnotation.value : father
father.hello2()
从上面的输出结果看出:Son
类重写了hello
方法,myAnnotation.value
的输出的值是son
,hello2
方法没有重写,myAnnotation.value
的输出的值是father
根据需求,我们肯定希望调用Son
类的所有方法时,都希望myAnnotation.value
的输出的值是son
,因此就需要重写父类的所有public
方法
那有没有办法不重写这些方法也能达到相同的效果呢,答案是可以的。
看看使用@within
和@target
的区别
我们分别在父类和子类上加上注解和去掉注解,一起来看看对应的结果
@within
父类无注解,子类有注解:
father.hello()
father.hello2()
before, myAnnotation.value : son
son.hello()
father.hello2()
父类有注解,子类无注解:
before, myAnnotation.value : father
father.hello()
before, myAnnotation.value : father
father.hello2()
before, myAnnotation.value : father
son.hello()
before, myAnnotation.value : father
father.hello2()
父类有注解,子类有注解(其实就是上面那个例子的结果):
before, myAnnotation.value : father
father.hello()
before, myAnnotation.value : father
father.hello2()
before, myAnnotation.value : son
son.hello()
before, myAnnotation.value : father
father.hello2()
@target
把切面代码改成如下:
@Order(-1)
@Aspect
@Component
public class MyAspect {
@Before(“@target(myAnnotation)”)
public void switchDataSource(JoinPoint point, MyAnnotation myAnnotation) {
System.out.println("before, myAnnotation.value : " + myAnnotation.value());
}
}
我们再一起来看看测试结果:
父类无注解,子类有注解:
father.hello()
father.hello2()
before, myAnnotation.value : son
son.hello()
before, myAnnotation.value : son
father.hello2()
父类有注解,子类无注解:
before, myAnnotation.value : father
father.hello()
before, myAnnotation.value : father
father.hello2()
son.hello()
father.hello2()
父类有注解,子类有注解
before, myAnnotation.value : father
father.hello()
before, myAnnotation.value : father
father.hello2()
before, myAnnotation.value : son
son.hello()
before, myAnnotation.value : son
father.hello2()
我们从上面总结出一套规律:
@within
:@Before
通知方法的myAnnotation
参数指的是调用方法所在的类上面的注解,就是这个方法是在哪个类上定义的
@target
:@Before
通知方法的myAnnotation
参数指的是调用方法运行时所属于的类上面的注解
我们最后总结一下,如果父类和子类上都标有注解,@within
和@target
的所得到实际注解的区别
| | @within | @target |
| — | — | — |
| 父类方法 | 父类注解 | 父类注解 |
| 子类不重写方法 | 父类注解 | 子类注解 |
| 子类重写方法 | 子类注解 | 子类注解 |
@target
看起来跟合理一点
从上面的分析可以看出,其实用@target
更符合我们想要的结果,在某个类上面加一个注解,拦截的时候就会获取这个类上面的注解,跟父类完全没有关系了
但这个时候会遇到一个问题,就是不相关的类都会生从代理类,
例子如下:
public class NormalBean {
public void hello() {
}
}
@Configuration
@EnableAspectJAutoProxy(exposeProxy = true)
public class Config {
@Bean
public Father father() {
return new Father();
}
@Bean
public Son son() {
return new Son();
}
@Bean
public NormalBean normalBean() {
return new NormalBean();
}
}
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class,
MyAspect.class);
Father father = context.getBean(“father”, Father.class);
father.hello();
father.hello2();
Son son = context.getBean(Son.class);
son.hello();
son.hello2();
NormalBean normalBean = context.getBean(NormalBean.class);
System.out.println(normalBean.getClass());
}
}
输出:
class cn.eagleli.spring.aop.demo.NormalBean E n h a n c e r B y S p r i n g C G L I B EnhancerBySpringCGLIB EnhancerBySpringCGLIBeebc2a39
可以看出NormalBean
自己什么都没做,但却被代理了
我们再把@target
换成@within
:
class cn.eagleli.spring.aop.demo.NormalBean
可以看出使用@within
时,不相关的类没有被代理
我们一起来看看为什么
在AbstractAutoProxyCreator
类中的wrapIfNecessary
方法打断点,看看什么情况:
@within
@target
我们从上面的图片就可以理解为什么@target
会生成代理类
我们再深入看一下:
@within
会走到如下:
public class ExactAnnotationTypePattern extends AnnotationTypePattern {
@Override
public FuzzyBoolean matches(AnnotatedElement annotated, ResolvedType[] parameterAnnotations) {
// …
}
}
小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Java工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年最新Java开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Java)
最后
为什么我不完全主张自学?
①平台上的大牛基本上都有很多年的工作经验了,你有没有想过之前行业的门槛是什么样的,现在行业门槛是什么样的?以前企业对于程序员能力要求没有这么高,甚至十多年前你只要会写个“Hello World”,你都可以入门这个行业,所以以前要入门是完全可以入门的。
②现在也有一些优秀的年轻大牛,他们或许也是自学成才,但是他们一定是具备优秀的学习能力,优秀的自我管理能力(时间管理,静心坚持等方面)以及善于发现问题并总结问题。
如果说你认为你的目标十分明确,能做到第②点所说的几个点,以目前的市场来看,你才真正的适合去自学。
除此之外,对于绝大部分人来说,报班一定是最好的一种快速成长的方式。但是有个问题,现在市场上的培训机构质量参差不齐,如果你没有找准一个好的培训班,完全是浪费精力,时间以及金钱,这个需要自己去甄别选择。
我个人建议线上比线下的性价比更高,线下培训价格基本上没2W是下不来的,线上教育现在比较成熟了,此次疫情期间,学生基本上都感受过线上的学习模式。相比线下而言,线上的优势以我的了解主要是以下几个方面:
①价格:线上的价格基本上是线下的一半;
②老师:相对而言线上教育的师资力量比线下更强大也更加丰富,资源更好协调;
③时间:学习时间相对而言更自由,不用裸辞学习,适合边学边工作,降低生活压力;
④课程:从课程内容来说,确实要比线下讲的更加深入。
应该学哪些技术才能达到企业的要求?(下图总结)
都可以入门这个行业,所以以前要入门是完全可以入门的。
②现在也有一些优秀的年轻大牛,他们或许也是自学成才,但是他们一定是具备优秀的学习能力,优秀的自我管理能力(时间管理,静心坚持等方面)以及善于发现问题并总结问题。
如果说你认为你的目标十分明确,能做到第②点所说的几个点,以目前的市场来看,你才真正的适合去自学。
除此之外,对于绝大部分人来说,报班一定是最好的一种快速成长的方式。但是有个问题,现在市场上的培训机构质量参差不齐,如果你没有找准一个好的培训班,完全是浪费精力,时间以及金钱,这个需要自己去甄别选择。
我个人建议线上比线下的性价比更高,线下培训价格基本上没2W是下不来的,线上教育现在比较成熟了,此次疫情期间,学生基本上都感受过线上的学习模式。相比线下而言,线上的优势以我的了解主要是以下几个方面:
①价格:线上的价格基本上是线下的一半;
②老师:相对而言线上教育的师资力量比线下更强大也更加丰富,资源更好协调;
③时间:学习时间相对而言更自由,不用裸辞学习,适合边学边工作,降低生活压力;
④课程:从课程内容来说,确实要比线下讲的更加深入。
应该学哪些技术才能达到企业的要求?(下图总结)
[外链图片转存中…(img-1eFtwuyi-1711157826699)]
[外链图片转存中…(img-tex5lLZP-1711157826699)]