Spring v4.2框架–注解IoC&AOP
1.IoC注解开发
1.IoC注解开发入门
a)创建一个Web项目,引入jar包
在Spring4的版本中,除了要引入基本的开发包外,还需要引入一个aop包。
b)引入Spring的配置文件
在src目录下创建applicationContext.xml
使用注解开发,需要引入一个context约束。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- bean definitions here -->
</beans>
c)创建一个接口和实现类
d)开启Spring的组件扫描
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- bean definitions here -->
<!-- Springde IoC注解入门 -->
<!-- 使用IoC的注解开发,配置扫描(哪些包下的类要使用注解) -->
<context:component-scan base-package="com.nikehu.spring.demo1"/>
</beans>
e)在类上添加注解
UserDaoImpl.java
package com.nikehu.spring.demo1;
import org.springframework.stereotype.Component;
/**
* 用户Dao实现类
* @author 猪猪
*
*/
@Component("userDao")//相当于<bean id="userDao" class="com.nikehu.spring.demo1.UserDaoImpl"/>
public class UserDaoImpl implements UserDao {
public void save() {
System.out.println("DAO中保存用户的方法执行了");
}
}
f)编写测试类
//spring的IoC注解方式
@Test
public void demo2(){
ApplicationContext aContext = new ClassPathXmlApplicationContext("applicationContext.xml");
UserDao bean = (UserDao) aContext.getBean("userDao");
bean.save();
}
2.IoC注解开发如何设置属性
使用注解方式可以没有set()方法,
- 如果属性有set()方法,属性注入的注解就加在set()方法上,
- 如果没有set()方法,属性注入的注解就加在属性上。
package com.nikehu.spring.demo1;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
/**
* 用户Dao实现类
* @author 猪猪
*
*/
@Component("userDao")//相当于<bean id="userDao" class="com.nikehu.spring.demo1.UserDaoImpl"/>
public class UserDaoImpl implements UserDao {
private String name;
@Value("zhangsan")
public void setName(String name) {
this.name = name;
}
public void save() {
System.out.println("DAO中保存用户"+name+"的方法执行了");
}
}
package com.nikehu.spring.demo1;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
/**
* 用户Dao实现类
* @author 猪猪
*
*/
@Component("userDao")//相当于<bean id="userDao" class="com.nikehu.spring.demo1.UserDaoImpl"/>
public class UserDaoImpl implements UserDao {
@Value("zhangsan")
private String name;
public void save() {
System.out.println("DAO中保存用户"+name+"的方法执行了");
}
}
3.IoC的注解的详解
1.@Component:组件
- 用于修饰一个类,将这个类交给Spring管理(前提:当前类所在的包已经添加了包扫描)
- 这个注解有三个衍生注解(功能类似):
-
- @Controller:? Web层
- @Service:Service层
- @Repository:dao层
2.属性注入的注解
- 普通属性:
-
- @Value:设置普通属性的注解
- 对象类型属性:
-
- @Autowired:设置对象类型属性的注解,但是它是按照类型完成属性的注入。
-
- 我们的习惯是按照名称来完成属性的注入:必须让@Autowired注解和@Qualifier()一起使用完成按照名称属性注入。
- @Resource():完成对象类型的属性注入,按照名称完成属性注入(需要加入javax的扩展包)
@Test
public void demo3(){
ApplicationContext aContext = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService service = (UserService) aContext.getBean("userService");
service.save();
}
package com.nikehu.spring.demo1;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
@Service("userService")
public class UserServiceImpl implements UserService {
//注入dao
@Autowired
@Qualifier("userDao")
private UserDao dao;
public void save() {
System.out.println("UserService的save()方法执行了。。");
dao.save();
}
}
package com.nikehu.spring.demo1;
public interface UserService {
void save();
}
package com.nikehu.spring.demo1;
import javax.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
@Service("userService")
public class UserServiceImpl implements UserService {
//注入dao
@Resource(name="userDao")
private UserDao dao;
public void save() {
System.out.println("UserService的save()方法执行了。。");
dao.save();
}
}
4.Bean的其他注解
生命周期注解
- @PostConstruct //相当于xml中的init-method
- @PreDestroy //相当于xml中的destory-method,必须关闭工厂才会销毁实例
package com.nikehu.spring.demo2;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import org.springframework.stereotype.Service;
@Service("customerService")
@Scope
public class CustomerService {
@PostConstruct //相当于xml中的init-method
public void init(){
System.out.println("Service的init方法执行了");
}
public void save(){
System.out.println("Service的save方法执行了");
}
@PreDestroy //相当于xml中的destory-method
public void destory(){
System.out.println("Service的destory方法执行了");
}
}
Bean的作用范围的注解
- @Scope:作用范围,取值有:
-
- singleton:单例,默认
- prototype:多例
- request
- session
- globalsession
5.Spring的IoC的XML和注解整合开发
二者比较:
- 适用场景:
- xml适用任何场景
-
- 结构清晰,后期维护方便
- 注解:有些地方用不了,这个类不是自己提供
-
- 纯注解(SSH),开发方便
xml和注解整合开发
xml用于配置Bean,可以看到我们将哪些类交给了Spring管理。
注解用于属性注入,使得类很干净,set方法也可以没有。
package com.nikehu.spring.demo3;
import javax.annotation.Resource;
public class ProductService {
@Resource(name="productDao")
private ProductDao productDao;
@Resource(name="orderDao")
private OrderDao orderDao;
public void save(){
System.out.println("productService的save方法执行了。。。");
productDao.save();
orderDao.save();
}
}
package com.nikehu.spring.demo3;
public class ProductDao {
public void save(){
System.out.println("productDao的save方法执行了。。。");
}
}
package com.nikehu.spring.demo3;
public class OrderDao {
public void save(){
System.out.println("orderDao的save方法执行了。。。");
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- bean definitions here -->
<!-- Spring 的 IoC注解入门 -->
<!-- 使用IoC的注解开发,配置扫描(哪些包下的类要使用注解),扫描是为了扫描类上的注解 -->
<!-- <context:component-scan base-package="com.nikehu.spring"/> -->
<!-- <context:component-scan base-package="com.nikehu.spring.demo2"/> -->
<!-- 在没有包扫描的情况下,使用属性注入:@Resource,@Value,@Autowired,@Qulifier -->
<context:annotation-config />
<bean id="productDao" class="com.nikehu.spring.demo3.ProductDao"/>
<bean id="orderDao" class="com.nikehu.spring.demo3.OrderDao"/>
<bean id="productService" class="com.nikehu.spring.demo3.ProductService"/>
</beans>
2.AOP的XML开发(AspectJ的XML的方式)
1.AOP概述
AOP(Aspect Oriented Programming,面向切面编程)
面向切面编程,通过预编译方式和运行时期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发的一个热点,也是spring框架中的一个重要内容,是函数式编程的一种衍生泛型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑个部分之间的耦合度降低,提高程序的可复用性,同时提高了开发效率。
AOP的应用场景
对程序进行增强:在不修改源码的情况下
AOP可以进行权限校验,日志记录,性能监控,事务控制
Spring的AOP的由来
AOP最早由AOP联盟组织提出的,制定了一套规范,Spring将AOP思想引入到框架中,必须遵守AOP联盟的规范。
底层实现:
- 动态代理:底层使用了两种动态代理
-
- JDK动态代理:缺陷,只能对实现了接口的类产生动态代理
- Cglib动态代理(类似于Javassist第三方的代理技术):对没有实现接口的对象产生动态代理。生成子类对象
2.JDK动态代理
package com.nike.spring.demo01;
public interface UserDao {
void save();
void update();
void delete();
void find();
}
package com.nike.spring.demo01;
public class UserDaoImpl implements UserDao {
public void save() {
System.out.println("这是保存用户");
}
public void update() {
System.out.println("这是修改用户");
}
public void delete() {
System.out.println("这是删除用户");
}
public void find() {
System.out.println("这是查找用户");
}
}
package com.nike.spring.demo01;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 使用JDK的动态代理对UserDao产生代理
* @author 猪猪
*
*/
public class JdkProxy implements InvocationHandler{
//将被增强的对象传递到代理当中
private UserDao dao;
public JdkProxy(UserDao dao){
this.dao = dao;
}
/**
* 产生UserDao的动态代理
* @return
*/
public UserDao createProxy(){
UserDao userDao = (UserDao) Proxy.newProxyInstance(
dao.getClass().getClassLoader(),
dao.getClass().getInterfaces(),
this);
return userDao;
}
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
//判断方法名
if("save".equals(method.getName())){
//增强
System.out.println("权限检验");
return method.invoke(dao, args);
}
return method.invoke(dao, args);
}
}
package com.nike.spring.demo01;
import org.junit.Test;
public class SpringDemo01 {
@Test
public void demo01(){
UserDao userDao = new UserDaoImpl();
userDao.find();
userDao.save();
userDao.update();
userDao.delete();
}
//动态代理
@Test
public void demo02(){
UserDao userDao = new UserDaoImpl();
//创建代理
userDao = (UserDao) new JdkProxy(userDao).createProxy();
userDao.find();
userDao.save();
userDao.update();
userDao.delete();
}
}
3.Cglib动态代理
- Cglib(Code Generation Library):是一个强大的,高性能的,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口。第三方开源代码生成库,动态添加类的属性和行为。
package com.nike.spring.demo02;
public class CustomerDao {
public void save(){
System.out.println("CustomerDao的save方法执行了。。。。");
}
public void find(){
System.out.println("CustomerDao的find方法执行了。。。。");
}
public void update(){
System.out.println("CustomerDao的update方法执行了。。。。");
}
public void delete(){
System.out.println("CustomerDao的delete方法执行了。。。。");
}
}
package com.nike.spring.demo02;
import java.lang.reflect.Method;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
/**
* Cglib动态代理
* @author 猪猪
*
*/
public class CglibProxy implements MethodInterceptor{
private CustomerDao dao;
public CglibProxy(CustomerDao dao) {
this.dao = dao;
}
/**
* 使用Cglib产生代理的方法
*/
public CustomerDao createProxy(){
//1.创建Cglib的核心对象
Enhancer enhancer = new Enhancer();
//2.设置父类
enhancer.setSuperclass(dao.getClass());
//3.设置回调(类似于InvocationHandler对象)
enhancer.setCallback(this);
//4.创建代理对象
CustomerDao create = (CustomerDao) enhancer.create();
return create;
}
public Object intercept(Object proxy, Method method, Object[] args,
MethodProxy methodProxy) throws Throwable {
//判断方法名
if("save".equals(method.getName())){
System.out.println("权限校验。。。。。。");
return methodProxy.invokeSuper(proxy, args);
}
return methodProxy.invokeSuper(proxy, args);
}
}
package com.nike.spring.demo02;
import org.junit.Test;
public class SpringDemo02 {
@Test
/**
* Cglib动态代理测试
*/
public void demo01(){
CustomerDao dao = new CustomerDao();
dao.save();
dao.delete();
dao.find();
dao.update();
}
@Test
/**
* Cglib动态代理测试
*/
public void demo02(){
CustomerDao dao = new CustomerDao();
CustomerDao proxy = new CglibProxy(dao).createProxy();
proxy.save();
proxy.delete();
proxy.find();
proxy.update();
}
}
4.Spring的AOP简介
AOP思想
Spring的AOP有自己的实现方式(非常繁琐)。
AspectJ是一个AOP框架,Spring引入AspectJ作为自身的AOP开发。
Spring有两套AOP开发方式
- Spring传统方式(弃用)
- Spring基于AspectJ的AOP开发(使用)
5.AOP的相关术语
JoinPoint(连接点):被拦截到的点(方法),Spring只支持方法类型的连接点
Pointcut(切入点):指我们要对哪些Joinpoint进行拦定义。
Advice(通知/增强):通知是指拦截到Joinpoint之后要做的事情就是通知,通知分为前置通知,后置通知,异常通知,最终通知,环绕通知(方法层面的增强)
Introduction(引介):一种特殊的通知在不修改类代码的前提下,Introduction可以在运行期为类动态的添加一些方法或属性。(类层面的增强)
Target(目标):代理的目标对象
Weaving(织入):指把增强应用到目标对象来创建新的代理对象的过程。
Spring采用的是动态代理织入,而AspectJ采用编译期织入和类装载期织入。
proxy(代理)一个类被AOP织入增强后,就产生一个结果代理类。
Aspect(切面):是切入点和通知的结合。
6.AOP开发入门(AspectJ的xml方式)
1)创建Web项目,引入jar包
-
引入基本开发包
-
引入aop开发的相关jar包
-
-
4个
-
引入aop的约束
-
<?xml version="1.0" encoding="UTF-8"?> <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" 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"> <!-- bean definitions here --> </beans>
-
2)编写目标类
package com.nike.spring.demo03;
public interface ProductDao {
public void save();
public void update();
public void find();
public void delete();
}
package com.nike.spring.demo03;
public class ProductDaoImpl implements ProductDao {
public void save() {
System.out.println("保存商品");
}
public void update() {
System.out.println("修改商品");
}
public void find() {
System.out.println("查找商品");
}
public void delete() {
System.out.println("删除商品");
}
}
<!-- 配置目标对象 -->
<bean id="productDao" class="com.nike.spring.demo03.ProductDaoImpl"></bean>
3)编写测试类
-
spring整合Junit
-
-
引入jar包:spring-test-4.2.4.RELEASE.jar
-
package com.nike.spring.demo03; import javax.annotation.Resource; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; //固定写法 @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext.xml") public class SpringDemo3 { @Resource(name="productDao") private ProductDao dao; @Test public void demo01(){ dao.save(); dao.delete(); dao.find(); dao.update(); } }
-
4)增强save()方法,编写一个切面类。
package com.nike.spring.demo03;
/**
* 切面类
* @author 猪猪
*
*/
public class MyAspectXML {
public void checkPri(){
System.out.println("权限校验。。。。。。。");
}
}
5)配置切面类,将切面类交给spring
<bean id="myAspect" class="com.nike.spring.demo03.MyAspectXML"></bean>
6)通过AOP的配置实现,然后直接运行测试类
<!-- 配置目标对象 -->
<bean id="productDao" class="com.nike.spring.demo03.ProductDaoImpl"></bean>
<bean id="myAspect" class="com.nike.spring.demo03.MyAspectXML"></bean>
<!-- 通过AOP的配置完成对目标类产生代理 -->
<aop:config>
<!-- expression表达式用于配置那些类的那些方法需要进行增强
*com.nike.spring.demo03.ProductDaoImpl.save(..)
*:代指任意返回值
..:代指任意参数
-->
<aop:pointcut expression="execution(* com.nike.spring.demo03.ProductDaoImpl.save(..))" id="pointcut1"/>
<!-- 配置切面
ref:切面类的id
-->
<aop:aspect ref="myAspect">
<aop:before method="checkPri" pointcut-ref="pointcut1"/>
</aop:aspect>
</aop:config>
7.通知的类型
前置通知:在目标方法执行之前进行操作;可以获得切入点的信息
<aop:before method="checkPri" pointcut-ref="pointcut1"/>
public void checkPri(JoinPoint point){
System.out.println(point);
System.out.println("权限校验。。。。。。。");
}
后置通知:在目标方法执行之后进行操作;可以获得切入点的信息,可以获取方法返回值
<!-- 后置通知 -->
<aop:after-returning method="writeLog" pointcut-ref="pointcut2" returning="result"/>
/**
* 后置通知
*/
public void writeLog(Object result){
System.out.println("日志记录。。。。。。。"+result);
}
环绕通知:在目标方法执行之前和之后进行操作;可以阻止目标方法的执行
/**
* 环绕通知
* 监控性能
* @param point
* @return
* @throws Throwable
*/
public Object around(ProceedingJoinPoint point) throws Throwable{
System.out.println("环绕前。。。。。。。");
Object obj = point.proceed();
System.out.println("环绕后。。。。。。。");
return obj;
}
<!-- 环绕通知 -->
<aop:around method="around" pointcut-ref="pointcut3"/>
异常抛出通知:在程序出现异常的时候进行操作;
最终通知:无论代码是否出现异常,最终通知总会执行;
引介通知:
<?xml version="1.0" encoding="UTF-8"?>
<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" 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"> <!-- bean definitions here -->
<!-- 配置目标对象 -->
<bean id="productDao" class="com.nike.spring.demo03.ProductDaoImpl"></bean>
<bean id="myAspect" class="com.nike.spring.demo03.MyAspectXML"></bean>
<!-- 通过AOP的配置完成对目标类产生代理 -->
<aop:config>
<!-- expression表达式用于配置那些类的那些方法需要进行增强
*com.nike.spring.demo03.ProductDaoImpl.save(..)
*:代指任意返回值
..:代指任意参数
-->
<aop:pointcut expression="execution(* com.nike.spring.demo03.ProductDaoImpl.save(..))" id="pointcut1"/>
<aop:pointcut expression="execution(* com.nike.spring.demo03.ProductDaoImpl.delete(..))" id="pointcut2"/>
<aop:pointcut expression="execution(* com.nike.spring.demo03.ProductDaoImpl.update(..))" id="pointcut3"/>
<aop:pointcut expression="execution(* com.nike.spring.demo03.ProductDaoImpl.find(..))" id="pointcut4"/>
<!-- 配置切面
ref:切面类的id
-->
<aop:aspect ref="myAspect">
<!-- 前置通知 -->
<aop:before method="checkPri" pointcut-ref="pointcut1"/>
<!-- 后置通知 -->
<aop:after-returning method="writeLog" pointcut-ref="pointcut2" returning="result"/>
<!-- 环绕通知 -->
<aop:around method="around" pointcut-ref="pointcut3"/>
<!-- 异常抛出通知 -->
<aop:after-throwing method="afterThrowing" pointcut-ref="pointcut4" throwing="ex"/>
<!-- 最终通知 -->
<aop:after-throwing method="after" pointcut-ref="pointcut4"/>
</aop:aspect>
</aop:config>
</beans>
package com.nike.spring.demo03;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
/**
* 切面类
* @author 猪猪
*
*/
public class MyAspectXML {
/**
* 前置通知
* @param point
*/
public void checkPri(JoinPoint point){
System.out.println(point);
System.out.println("权限校验。。。。。。。");
}
/**
* 后置通知
*/
public void writeLog(Object result){
System.out.println("日志记录。。。。。。。"+result);
}
/**
* 环绕通知
* 监控性能
* @param point
* @return
* @throws Throwable
*/
public Object around(ProceedingJoinPoint point) throws Throwable{
System.out.println("环绕前。。。。。。。");
Object obj = point.proceed();
System.out.println("环绕后。。。。。。。");
return obj;
}
/**
* 异常抛出通知
*/
public void afterThrowing(Throwable ex){
System.out.println("异常抛出通知。。。。。。。。"+ex.getMessage());
}
/**
* 最终通知:相对于finally代码块
*
*/
public void after(){
System.out.println("最终通知。。。。。。。。");
}
}
package com.nike.spring.demo03;
public class ProductDaoImpl implements ProductDao {
public void save() {
System.out.println("保存商品");
}
public void update() {
System.out.println("修改商品");
}
public void find() {
System.out.println("查找商品");
int i = 1/0;
}
public String delete() {
System.out.println("删除商品");
return "返回值";
}
}
8.切入点表达式写法
expression="execution(* com.nike.spring.demo03.ProductDaoImpl.find(..))"
语法格式:基于execution的函数完成
[访问修饰符] 方法返回值 包名.类名.方法名(参数)