莫道君行早,更有早行人
本篇主要是简单的尝试一下Spring的两大功能,来感觉一下Spring的强大,后面将进行更加详细的介绍。java
咱们都知道,Spring两大核心功能就是控制反转/依赖注入、面向切面编程。下面介绍一下两大功能。git
Don't call me , I will call you!
控制反转(Inversion of Control)/依赖注入(Dependency Injection),简称IoC/DI.正则表达式
控制反转不是一种技术,而是一种设计思想:将原来程序须要什么对象本身建立 转变为 须要什么对象向IoC容器获取,建立对象的工做由原来程序自身控制,反转到了由IoC容器进行建立。把相关的控制权进行了反转,反转给了Spring IoC容器。spring
DI:Dependency Injection。即依赖注入。对象(组件)之间的依赖关系由IoC容器来进行决定。express
好比:编程
在UserController中须要调用UserService(暂不考虑接口设计)。则在UserController中须要其自身经过new来建立UserService对象。网络
以下:学习
UserService:测试
public class UserService{ private PrintStream printStream; public UserService(PrintStream printStream){ this.printStream = printStream; } public void sayHello(){ printStream.println("sayHello!") } }
UserController:this
public class UserController{ private UserService userService; public UserController(){ this.userService = new UserService(System.out); } public void sayHi(){ userService.sayHello(); } }
在Spring中,程序的对象控制权由其自身反转到了Spring容器,也就是不须要应用程序来new对象。既然不须要应用程序自身来建立Bean了,那么程序在运行的过程当中,Bean从何而来呢?此时就是DI的show time了。
Spring中的DI正是来实现IoC的一种方式:Spring容器负责维护对象(Bean)之间的依赖关系,并经过DI来向对象中注入其所依赖的对象。
下面使用Spring的方式来设计:
public class UserService{ private PrintStream printStream; public UserService(PrintStream printStream){ this.printStream = printStream; } public void sayHello(){ printStream.println("sayHello!") } } public class UserController{ private UserService userService; public UserController(UserService userService){ this.userService = userService; } public void sayHi(){ userService.sayHello(); } }
spring.xml文件:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="userService" class="com.ooyhao.spring.bean.UserService"> <constructor-arg value="#{T(System).out}"/> </bean> <bean id="UserController" class="com.ooyhao.spring.bean.UserController"> <constructor-arg ref="userService"/> </bean> </beans>
测试类:
public void testXml(){ ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("spring.xml"); UserController userController = ctx.getBean(UserController.class); userController.sayHi(); }
若是须要面向接口设计的话,接口因为没法实例化,因此在编码的时候必须指定具体的实现类,如此一来,致使没法自由动态的切换实现类,耦合度过高。而Spring xml方式的话,实现类松耦合,简化了开发,后期若是须要修改的话,直接修改xml文件,不用修改代码。
SpringConfig配置类:
public class SpringConfig{ @Bean public UserService userService(){ UserService userService = new UserService(System.out); return userService; } @Bean public UserController userController(){ UserController userController = new UserController(userService()); return userController; } }
测试类:
public void testJavaConfig(){ AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class); UserController userController = ctx.getBean(UserController.class); userController.sayHi(); }
::: tip
确实Spring xml文件实现了松耦合,可是实际项目中能够发现,每每xml不多修改。因此,SpringBoot又主张Java配置类的方式,可是Java配置类的绝大部分都是由Spring xml转化过来的。因此,不论是Java配置类方式仍是Spring xml文件方式都有必要了解,固然我以为,因为为了后期更好的学习和使用SpringBoot,能够以Java配置类方式为主。
:::
参考自:阿古拉斯啦啦 http://www.javashuo.com/article/p-hfyrskjs-mu.html
Aop:是Aspect oriented Programming的缩写,即面向切面编程。经过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP(面向切面编程)是OOP(面向对象编程)的一种补充,而不是一种替代品。利用AOP能够对业务逻辑的各个部分进行隔离,从而下降各个模块之间的耦合度,简化维护。常见使用AOP的场景:事务控制,日志管理,权限控制等等。
下面先用几张图熟悉一下AOP是怎么回事。(图片来源于网络)
看完了上面的理论部分知识, 我相信仍是会有很多朋友感受到 AOP 的概念仍是很模糊, 对 AOP 中的各类概念理解的还不是很透彻. 其实这很正常, 由于 AOP 中的概念是在是太多了, 我当时也是花了老大劲才梳理清楚的.
下面我以一个简单的例子来比喻一下 AOP 中 Aspect, Joint point, Pointcut 与 Advice之间的关系.
让咱们来假设一下, 从前有一个叫爪哇的小县城, 在一个月黑风高的晚上, 这个县城中发生了命案. 做案的凶手十分狡猾, 现场没有留下什么有价值的线索. 不过万幸的是, 刚从隔壁回来的老王刚好在这时候无心中发现了凶手行凶的过程, 可是因为天色已晚, 加上凶手蒙着面, 老王并无看清凶手的面目, 只知道凶手是个男性, 身高约七尺五寸. 爪哇县的县令根据老王的描述, 对守门的士兵下命令说: 凡是发现有身高七尺五寸的男性, 都要抓过来审问. 士兵固然不敢违背县令的命令, 只好把进出城的全部符合条件的人都抓了起来.
来让咱们看一下上面的一个小故事和 AOP 到底有什么对应关系.
首先咱们知道, 在 Spring AOP 中 Joint point 指代的是全部方法的执行点, 而 point cut 是一个描述信息, 它修饰的是 Joint point, 经过 point cut, 咱们就能够肯定哪些 Joint point 能够被织入 Advice. 对应到咱们在上面举的例子, 咱们能够作一个简单的类比, Joint point 就至关于 爪哇的小县城里的百姓,pointcut 就至关于 老王所作的指控, 即凶手是个男性, 身高约七尺五寸, 而 Advice 则是施加在符合老王所描述的嫌疑人的动做: 抓过来审问.
为何能够这样类比呢?
AOP中的Joinpoint
能够有多种类型:构造方法调用,字段的设置和获取,方法的调用,方法的执行,异常的处理执行,类的初始化。也就是说在AOP的概念中咱们能够在上面的这些Joinpoint
上织入咱们自定义的Advice
,可是在Spring中却没有实现上面全部的joinpoint
,确切的说,Spring只支持方法执行类型的Joinpoint
。
Advice 的类型
before advice
, 在 join point
前被执行的 advice
. 虽然 before advice
是在 join point
前被执行, 可是它并不可以阻止 join point
的执行, 除非发生了异常(即咱们在 before advice
代码中, 不能人为地决定是否继续执行 join point
中的代码)after return advice
, 在一个 join point
正常返回后执行的 advice
after throwing advice
, 当一个 join point
抛出异常后执行的 advice
after(final) advice
, 不管一个 join point
是正常退出仍是发生了异常, 都会被执行的 advice
.around advice
, 在 join point
前和 joint point
退出后都执行的 advice
. 这个是最经常使用的 advice
.introduction
,introduction
能够为原有的对象增长新的属性和方法。在Spring中,经过动态代理和动态字节码技术实现了AOP
,这些内容,咱们将在之后进行讲解。
下面分别使用XML和Java配置类实现AOP
目标类:
public class Person { public String sayHello(){ System.out.println("Person say Hello!"); return "sayHelloMethod"; } public String sayBye(){ System.out.println("Person say ByeBye!"); return "sayByeMethod"; } }
切面类:
public class XmlLoggerAspect { public void before(){ System.out.println("--->before"); } public void after(){ System.out.println("--->after"); } public void afterReturning(Object returnVal){ System.out.println("--->afterReturning : " + returnVal); } public void afterThrowing(Exception exception){ System.out.println("--->afterTrowing:"+exception.getMessage()); } public Object around(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println("--->around before"); Object proceed = joinPoint.proceed(); System.out.println("around result : "+proceed); System.out.println("--->around after"); return proceed; } }
xml配置文件:
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!--业务类Target--> <bean id="person" class="com.ooyhao.spring.aop.Person"/> <!--切面类--> <bean id="loggerAspect" class="com.ooyhao.spring.aspect.XmlLoggerAspect"/> <aop:config> <aop:aspect ref="loggerAspect"> <!--public String com.ooyhao.spring.aop.Person.sayHello() * String com.ooyhao.spring.aop.Person.sayHello() * com.ooyhao.spring.aop.Person.sayHello() * *.sayHello() * *.say*() * *.say*(..) --> <aop:pointcut id="pointCut" expression="execution(* *.say*(..))"/> <aop:before method="before" pointcut-ref="pointCut"/> <aop:after method="after" pointcut-ref="pointCut"/> <aop:after-returning method="afterReturning" pointcut-ref="pointCut" returning="returnVal"/> <aop:after-throwing method="afterThrowing" pointcut-ref="pointCut" throwing="exception"/> <aop:around method="around" pointcut-ref="pointCut"/> </aop:aspect> </aop:config> </beans>
测试:
@Test public void testXmlAop(){ ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-aop.xml"); Person bean = context.getBean(Person.class); bean.sayHello(); System.out.println("================="); bean.sayBye(); /** * * --->before * --->around before * Person say Hello! * around result : sayHelloMethod * --->around after * --->afterReturning : sayHelloMethod * --->after * ================= * --->before * --->around before * Person say ByeBye! * around result : sayByeMethod * --->around after * --->afterReturning : sayByeMethod * --->after */ //sayHello出现 int i = 1/0;时 /** * * --->before * --->around before * Person say Hello! * --->afterTrowing:/ by zero * --->after * * java.lang.ArithmeticException: / by zero * at com.ooyhao.spring.aop.Person.sayHello(Person.java:7) * * */ }
由上测试结果能够看出:
before
-- > around before
--> target method
--> around after
--> afterReturning
--> after
before
--> around before
--> target method
--> afterTrowing
--> after
java配置方式的切面:
@Aspect @Component @EnableAspectJAutoProxy public class ConfigLoggerAspect { @Pointcut("execution(**.say*()))") public void pointCut(){} @Before("pointCut()") public void before(){ System.out.println("--->before"); } @After("pointCut()") public void after(){ System.out.println("--->after"); } @AfterReturning(value = "pointCut()", returning = "returnVal") public void afterReturning(Object returnVal){ System.out.println("--->afterReturning : " + returnVal); } @AfterThrowing(value = "pointCut()", throwing = "exception") public void afterThrowing(Exception exception){ System.out.println("--->afterTrowing:"+exception.getMessage()); } @Around(value = "pointCut()") public Object around(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println("--->around before"); Object proceed = joinPoint.proceed(); System.out.println("around result : "+proceed); System.out.println("--->around after"); return proceed; } }
@Aspect
: 定义为一个切面@Component
:定义为一个Spring
组件@EnableAspectJAutoProxy
:开启Aop
自动代理模式Java配置类:
@ComponentScan(basePackages = "com.ooyhao.spring") public class AopConfig { @Bean public Person person(){ return new Person(); } }
@ComponentScan(basePackages = "com.ooyhao.spring")
: 将前面的切面进行扫描成组件。测试类:
@Test public void testJavaConfigAop(){ AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AopConfig.class); Person bean = context.getBean(Person.class); bean.sayHello(); bean.sayBye(); }
:::tip
提示:
前面均使用的是AspectJ表达式,这样能够定位到有必定规律的目标方法,下降程序耦合,可是操做不是特别灵活,我的比较使用注解方式,能够指定到某一个目标方法。
@pointcut("@annotation(com.sample.security.AdminOnly)")
// 匹配注解有AdminOnly注解的方法
:::
源码地址: https://gitee.com/ooyhao/JavaRepo_Public/tree/master/Spring-in-Action
若是以为不错的话,那就关注一下小编哦!一块儿交流,一块儿学习