面试题:1、谈谈你对IOC和AOP的理解
参考资料:
尝试讲清楚spring核心概念IOC和AOP
Spring JdbcTemplate详解
Java之JDBC
【Spring基础】AOP使用注解实战
答:IOC是spring的一个容器,可以帮助我们创建对象,而不需要我们手动去创建。IOC有一个强大的功能依赖注入(DI),可以通过依赖注入更好的去管理Bean,达到解耦的目的。举个例子:我们用的JDBCTemplate可以通过xml的方式配置,然后去注入使用,但是需要数据源,我们可以随意切换数据源,使得JDBCTemplate不强制依赖某一数据源,达到解耦。
答:AOP常常用在事务和日志中,因为他可以不植入代码中执行,
2、IOC的过程给讲讲;还有B+Tree的指针有利于范围查找;zookeeper和eureka和区别,一个是http,一个是RPC。
3、Aop中可以改变目标方法的参数吗?
一、IOC(设计模式:工厂模式)
1.1、概念:是一个spring的容器,帮助我们创建对象
二、Aop概念
Aop是一个概念,springAop和aspectJ是Aop的实现。如果说前端发送过来请求到controller再到service再到dao是由上而下的话,那么springAop是从水平切入,但是和其是充分解耦的,从而实现一些功能,比如日志,事务,异常,权限等。 实现切面的一些功能。比如日志,事务,权限,异常等。
三、Aop和aspectJ的联系
Aop的语法比较复杂,所以借助了aspectJ的语法风格,但是底层还是自己实现的。
四、Aop的使用
4.1、开启Aop
通过@Configuration注解来开启支持aspectJ,并且加注解@EnableAspectJAutoProxy。
@Configuration
@EnableAspectJAutoProxy
public class AopConfig {
}
4.2、声明一个Aspect(切面)类
声明一个Aspect注释类,并且把该类定义成一个Bean交给spring管理。
4.2.1、切面类包含通知和切入点
@Component
@Aspect
public class AspectTest {
}
4.3、在切面类编写切入点和通知
4.3.1、切入点就是在哪些目标方法将要进行执行通知
4.3.2、通知就是围绕目标方法执行的方法
4.3.4、代码如下:(可以发现切入点表达式都是一样,我怎么去消除呢?)
@Component
@Aspect
public class AspectTest {
@Before("execution(* com.xuecheng.aop.service..*.*(..))")
public void before() {
System.out.println("before执行。。。。");
}
@After("execution(* com.xuecheng.aop.service..*.*(..))")
public void after() {
System.out.println("after执行。。。。");
}
}
4.3.5、优化后的代码如下:
@Component
@Aspect
public class AspectTest {
@Pointcut("execution(* com.xuecheng.aop.service..*.*(..))")
public void pointCut() {
}
@Before("pointCut()")
public void before(){
System.out.println("before执行。。。。");
}
@After("pointCut()")
public void after(){
System.out.println("after执行。。。。");
}
}
4.4、编写目标方法
4.4.1、接口代码
public interface FatherServ {
public void ss();
}
4.4.2、实现类(目标类)代码
@Component("s")
public class TestService implements FatherServ {
public void ss(){
System.out.println("目标方法执行...");
}
}
4.5、测试结果如下:
before执行。。。。
目标方法执行...
after执行。。。。
五、Aop中的切入点表达式分析
参考资料:Spring AOP切点表达式详解
在执行表达式的时候,我们可以通过逻辑运算符&&(and) , ||(or) , !(not)对表达式进行搭配。比如:
execution(* com.jpeony.spring.ConferenceServiceImpl.conference(..) && within(com.jpeony.spring.*))
增加了一个限制就是我们只管com.jpeony.spring下的包,这里的&&可以使用and来替代,
同理|| , !都是一样的用法,灵活多变,只能根据实际情况看着办。
5.1、execution(粒度可以精确到具体的方法)
5.2、within(粒度只能精确到类)
5.2.1、精确到BusinessObject类,进行该类下的所有方法增强。
within(com.spring.service.BusinessObject)
5.2.2、精确到service包下的所有类,但是不包括子包下的类
within(com.spring.service.*)
5.2.3、精确到service包下的类以及该包下的子包的所有类
within(com.spring.service..*)
5.3、args(粒度是匹配参数的个数和参数的类型的方法)
5.3.1、精确匹配到只有一个String类型的参数的方法
args(java.lang.String)
5.3.2、精确匹配到参数开头是String类型,结尾是Integer类型,中间可以是任意个数和任意类型的参数。这里通配符用“…”,不能用“ * ”
args(java.lang.String,..,java.lang.Integer)
5.4、this和target的区别
5.4.1、用于环绕通知时,this和target里指定目标对象时,如果目标对象没有实现接口,则执行结果一样;如果目标对象实现了一个接口,this就不能执行环绕通知,但是target是可以的。
5.5、@within
5.5.1、粒度匹配到指定注解放在哪些类上,就对哪些类进行增强。
5.6、@annotation
5.6.1、与@within相似,@annotation是作用于方法上的。
5.7、@args
5.7.1、表示用注解标注的类作为参数时,被执行方法增强
5.8、@DeclareParents
5.8.1、表示为指定的目标类引入新的属性和方法。
5.9、perthis和pertarget
5.9.1、在多线程的情况下,进行创建Aspect类的多例
六、环绕通知
6.1、切面类如下:
步骤一: 添加注解@Around
步骤二:在通知方法上传入参数ProceedingJoinPoint,然后调用其方法proceed();
@Component
@Aspect
public class AspectTest {
@Around("pointCut()")
public void aroundTest(ProceedingJoinPoint proceedingJoinPoint) {
try {
System.out.println("环绕通知之前执行");
proceedingJoinPoint.proceed();
System.out.println("环绕通知之后执行");
} catch (Throwable throwable) {
System.out.println("异常通知执行。。");
}
}
@Pointcut("execution(* com.xuecheng.aop.service..*.*(..))")
public void pointCut() {
}
}
七、可以获取目标方法的参数,并且可以对其修改
7.1、在通知方法内传入参数JoinPoint,并调用方法getArgs();获取目标方法的参数,并对其进行修改。
@Around("execution(* com.xuecheng.aop.service..*.*(..))")
public void aroundTest(JoinPoint JoinPoint) {
//获取目标方法的参数,并可以对其进行修改
// Object[] args = joinPoint.getArgs();
}
八、通知(Advice)的分类
8.1、Spring切面定义了5种类型通知:
1)前置通知(Before):在目标方法被调用之前调用通知功能。
2)后置通知(After):在目标方法完成之后调用通知,不会关心方法的输出是什么。(不管目标方法是否成功执行,后置通知一定执行。)
3)返回通知(After-returning): 在目标方法成功执行之后调用通知。(在后置通知之前执行,必须方法执行成功后,才能执行!)
4)异常通知(After-throwing):在目标方法抛出异常后调用通知。
5)环绕通知(Around):通知包裹了被通知的方法,在被通知的方法调用之前和之后执行自定义的行为。(ProceedingJoinPoint参数可以有,但是JoinPoint不能存在。)
ProceedingJoinPoint 继承于 JoinPoint ;ProceedingJoinPoint里面有两个方法:proceed(); 和 proceed(Object object);
九、AOP术语
切点、连接点、切面、通知
连接点是切点的集合,连接点是一个飘渺的范围大的概念。切面是通知和切点的结合。通知告诉我们的是,“什么时候做?”和“做什么?”;而切点告诉我们的是,“在哪做?”
十、Aop的切面类的执行顺序
10.1、可以通过注解@order(1)解决执行的顺序,里面的数字越低,优先级越高。
十一、springAop改变目标方法的参数和对目标方法的返回值的返回后进行修改(仅适用于around(环绕通知))
也可以将改变后的参数传入目标方法的参数内执行
@Around("pointCut()")
public void before(ProceedingJoinPoint joinPoint) {
Object[] args = joinPoint.getArgs();
for (Object arg : args) {
System.out.println("方法的参数是"+arg);
}
System.out.println("环绕之前。。。");
try {
String proceed = (String) joinPoint.proceed();
proceed="改变后的返回值";
System.out.println(proceed);
} catch (Throwable throwable) {
throwable.printStackTrace();
}
System.out.println("环绕之后。。。");
}
测试代码
public class Demo {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AopConfig.class);
TestService s = (TestService) applicationContext.getBean("s");
s.ss("String参数。。");
}
}
测试结果
方法的参数是String参数。。
环绕之前。。。
目标方法执行...String参数。。
改变后的返回值
环绕之后。。。