一、Spring的IOC的注解开发
1、Spring配置文件引入context约束
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p" 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">
</beans>
2、创建web项目,引入jar包
3、Spring配置文件中开启注解扫描
<!-- 开启扫描注解 -->
<!--使用IOC的注解开发,配置组件扫描(哪些包下的类使用IOC的注解) -->
<!--扫描是为了扫描类上的注解 -->
<!--扫描的是一个包,demo1包--!>
<context:component-scan base-package="spring.demo1"></context:component-scan>
<!-- 开启扫描注解 扫描的是spring下的所有子包-->
<context:component-scan base-package="spring"></context:component-scan>
4、在类上添加注解
public interface UserDao {
public void save();
}
/**
* 用户Dao的实现类
*
* 注解方式设置属性的值
*
* @author Administrator
*
*/
@Component("userDao")// 相当于<bean id="userDao" class="spring.demo1.userDaoImpl" />
public class UserDaoImpl implements UserDao {
// 采取DI(依赖注入)的来设置name
@Value("吴彦祖")
private String name;
@Override
public void save() {
System.out.println("UserDaoImpl执行了......" + name);
}
}
5、编写测试类
// Spring的IOC的注解方式
@Test
public void demo1() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
UserDao userDao = (UserDao) applicationContext.getBean("userDao");
userDao.save();
}
6、注解方式设置属性的值
注解方式:使用注解方式,可以没有set方法的
属性如果有set方法,需要将属性注入的注解添加到set方法。
属性如果没有set方法,需要将属性注入的注解添加到属性上。
@Value("xxx")
/**
* 用户Dao的实现类
*
* 注解方式设置属性的值
*
* @author Administrator
*
*/
@Component("userDao") // 相当于<bean id="userDao" class="spring.demo1.userDaoImpl"/>
public class UserDaoImpl implements UserDao {
// 采取DI(依赖注入)的来设置name
@Value("吴彦祖")
private String name;
/*
* public void setName(String name) { this.name = name; }
*/
@Override
public void save() {
System.out.println("UserDaoImpl执行了......" + name);
}
}
二、Spring的IOC的注解的详解
1、@Component:组件
修饰一个类,将这个类交给Spring管理
这个注解有三个衍生注解(功能类似),修饰类
- @Controller:web层
- @Service:service层
- @Respository:dao层
2、属性注入的注解
普通属性:
- @Value:设置普通属性的值。
对象类型属性:
- @Autowired:设置对象类型的属性的值,但是按照类型完成属性的注入。
我们习惯是按照名称完成属性注入:必须让@Autowired注解和@Qualifier一起使用完成按照名称属性注入。
还有一个方法: - @Resource:完成对象类型的属性的注入,并且是按照名称完成属性注入。
package spring.demo1;
public interface UserService {
public void save();
}
package 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") // <bean id="userService" class=""/>
public class UserServiceImpl implements UserService {
// 注入Dao
/*
* @Autowired
*
* @Qualifier(value="userDao")
*/
@Resource(name = "userDao")
private UserDao userDao;
@Override
public void save() {
System.out.println("UserService的save方法执行了...");
userDao.save();
}
}
3、Bean的其他注解
生命周期相关的注入(了解)
@PostConstruct:初始化方法
@PreDestroy:销毁方法
Bean的作用范围的注解
@Scope:作用范围
singleton:默认单例
prototype:多例
request:
session:
globalsession:
package spring.demo2;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
@Service("customerService") // <bean id="" class="" init-method=""destroy-method=""/>
@Scope("prototype")
public class CustomerService {
@PostConstruct // 相当于init-method
public void init() {
System.out.println("CustomerService被初始化了...");
}
public void save() {
System.out.println("Service的save方法执行了...");
}
@PreDestroy // 相当于destroy-method
public void destroy() {
System.out.println("CustomerService被销毁了...");
}
}
package spring.demo2;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringDemo2 {
@Test
public void demo1() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
CustomerService customerService1 = (CustomerService) applicationContext.getBean("customerService");
System.out.println(customerService1);
CustomerService customerService2 = (CustomerService) applicationContext.getBean("customerService");
System.out.println(customerService2);
System.out.println(customerService1 == customerService2);
((AbstractApplicationContext) applicationContext).close();
}
}
4、IOC的XML和注解开发的比较
- 适用比较:
xml:可以适用任何场景,结构清晰,维护方便
注解:有些地方用不了,这个类不是自己提供的开发方便 - xml和注解整合开发
xml管理Bean,注解完成属性注入(开发常用)
<!--在没有扫描的情况下,使用属性注入的注解: @Resource,@Value,@Autowired,@Qulifier -->
<context:annotation-config></context:annotation-config>
三、Spring的AOP的XML的开发
1、AOP:Aspect Oriented Programming
面向切面编程,通过预编译的方式和运行期的动态代理实现程序功能的统一维护的一种技术。
AOP是OOP的扩展和延伸,解决OOP开发中遇到的问题。(OOP:Object Oriented Pragramming)
2、为什么学习AOP
对程序进行增强,不修改源码的情况下
AOP可以进行权限校验,日志记录,性能监控,事务控制。
3、Spring的底层的AOP实现原理
动态代里
JDK动态代理:只能对实现了接口的类产生代理
Cglib动态代理(类似于Javassist第三方代理技术):对没有实现接口的类产生代理对象,生产子类对象。
Spring底层默认对实现接口的类使用JDK动态代理,如果没有实现接口,默认采用cglib动态代理,可以来回进行切换。
1、JDK动态代理
接口DAO
package spring.demo1;
public interface UserDao {
public void save();
public void find();
public void update();
public void delete();
}
接口DAO的实现类
package spring.demo1;
public class UserDaoImpl implements UserDao {
@Override
public void save() {
System.out.println("保存客户");
}
@Override
public void find() {
System.out.println("查找客户");
}
@Override
public void update() {
System.out.println("修改客户");
}
@Override
public void delete() {
System.out.println("删除客户");
}
}
代理类
package spring.demo1;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 使用JDK动态代理对UserDao产生代理
*
* @author Administrator
*
*/
public class JdkProxy implements InvocationHandler {
// 将被增强的对象传递到代理中
private UserDao userDao;
public JdkProxy(UserDao userDao) {
this.userDao = userDao;
}
/*
* public static Object newProxyInstance(ClassLoader loader, Class<?>[]
* interfaces, InvocationHandler h) throws IllegalArgumentException
* loader:一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载
* interfaces:一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,
* 那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了
* h: 一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上
*/
/**
* 产生UserDao代理的方法
*/
public UserDao createProxy() {
UserDao userDaoProxy = (UserDao) Proxy.newProxyInstance(userDao.getClass().getClassLoader(),
userDao.getClass().getInterfaces(), this);
return userDaoProxy;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 判断方法名是不是save:
if ("save".equals(method.getName())) {
System.out.println("权限校验=============");
return method.invoke(userDao, args);
}
return method.invoke(userDao, args);
}
}
测试类
package spring.demo1;
import org.junit.Test;
public class SpringDemo1 {
/**
* 在使用DAO的save方法之前进行一个权限校验操作
*/
// 传统操作
@Test
public void demo1() {
UserDao userDao = new UserDaoImpl();
// 在这里直接插入代码,无需代理
userDao.save();
userDao.find();
userDao.update();
userDao.delete();
}
@Test
public void demo2() {
UserDao userDao = new UserDaoImpl();
// 创建代理
UserDao proxy = new JdkProxy(userDao).createProxy();
proxy.save();
proxy.find();
proxy.update();
proxy.delete();
}
}
2、cglib动态代理
客户DAO(就不写实现类了,直接测试)
package spring.demo2;
public class CustomerDao {
public void save() {
System.out.println("保存客户");
}
public void find() {
System.out.println("查找客户");
}
public void update() {
System.out.println("修改客户");
}
public void delete() {
System.out.println("删除客户");
}
}
Cglib动态代理类
package spring.demo2;
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 Administrator
*
*/
public class CglibProxy implements MethodInterceptor {
private CustomerDao customerDao;
public CglibProxy(CustomerDao customerDao) {
this.customerDao = customerDao;
}
/**
* 使用cglib产生代理的方法
*/
public CustomerDao createProxy() {
// 1、创建cglib的核心类对象
Enhancer enhancer = new Enhancer();
// 2、设置父类
enhancer.setSuperclass(customerDao.getClass());
// 3、设置回调(类似于InvocationHandler对象)
enhancer.setCallback(this);
//4、创建代理对象
CustomerDao proxy=(CustomerDao) enhancer.create();
return proxy;
}
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
// 判断方法是否为save方法
if ("save".equals(method.getName())) {
// 增强
System.out.println("权限校验======");
return methodProxy.invokeSuper(proxy, args);
}
return methodProxy.invokeSuper(proxy, args);
}
}
package spring.demo2;
import org.junit.Test;
public class SpringDemo2 {
/**
* cglib的测试
*/
@Test
public void demo1() {
CustomerDao customerDao = new CustomerDao();
CustomerDao proxy = new CglibProxy(customerDao).createProxy();
proxy.save();
proxy.find();
proxy.update();
proxy.delete();
}
}
4、Spring的AOP的开发(AspectJ的XML的方式)
AOP思想最早是由AOP联盟组织提出的,Spring是使用这种思想最好的框架。
1、Spring的AOP有自己实现的方式(非常繁琐)。AspectJ是一个AOP的框架,Spring引入AspectJ作为自身AOP的开发。
2、Spring有两套AOP的开发方式
Spring传统方式(弃用)
Spring基于AspectJ的AOP的开发(使用)
5、AOP的相关术语
6、Spring的AOP的入门(AspectJ的XML的方式)
第一步、创建web项目,引入jar包
引入基本开发包
引入aop开发的相关jar包
第二步、引入Spring的配置文件
引入aop的约束
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
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/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
</beans>
第三步、编写目标类
目标DAO
package spring.demo3;
public interface ProductDao {
public void save();
public void find();
public void update();
public void delete();
}
目标DAO的实现类
package spring.demo3;
public class ProductDaoImpl implements ProductDao {
@Override
public void save() {
// TODO Auto-generated method stub
System.out.println("保存商品");
}
@Override
public void find() {
// TODO Auto-generated method stub
System.out.println("查找商品");
}
@Override
public void update() {
// TODO Auto-generated method stub
System.out.println("修改商品");
}
@Override
public void delete() {
// TODO Auto-generated method stub
System.out.println("删除商品");
}
}
第四步、编写测试类
Spring整合JUnit单元测试
引入jar包
package spring.demo3;
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;
/**
* AOP的入门
*
* @author Administrator
*
*/
// Spring整合JUnit单元测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class SpringDemo3 {
@Resource(name = "productDao")
private ProductDao productDao;
@Test
public void demo1() {
productDao.save();
productDao.update();
productDao.delete();
productDao.find();
}
}
第五步、编写一个切面类和AOP的配置
编写切面
package spring.demo3;
/**
* 切面类
*
* @author Administrator
*
*/
public class MyAspectXML {
public void checkPri() {
System.out.println("权限校验=========");
}
}
<!--将切面类交给Spring管理 -->
<bean id="myAspect" class="spring.demo3.MyAspectXML"></bean>
<!--通过AOP的配置完成对目标类产生代理 -->
<aop:config>
<!--表达式配置哪些类的哪些方法需要进行增强 -->
<aop:pointcut expression="execution(* spring.demo3.ProductDaoImpl.save(..))"
id="pointcut1" />
<!--配置切面 -->
<aop:aspect ref="myAspect">
<aop:before method="checkPri" pointcut-ref="pointcut1" />
</aop:aspect>
</aop:config>
第六步、输出结果
7、Spring中通知类型
1、前置通知:在目标方法执行之前进行操作
前置通知:获得切入点信息
/**
* 切面类
*
* @author Administrator
*
*/
public class MyAspectXML {
/**
* 前置通知
*/
public void checkPri(JoinPoint joinpoint) {
System.out.println("权限校验=========" + joinpoint);
}
}
<aop:config>
<!--表达式配置哪些类的哪些方法需要进行增强 -->
<aop:pointcut expression="execution(* spring.demo3.ProductDaoImpl.save(..))"
id="pointcut1" />
<!--配置切面 -->
<aop:aspect ref="myAspect">
<!--前置通知 -->
<aop:before method="checkPri" pointcut-ref="pointcut1" />
</aop:aspect>
</aop:config>
2、后置通知:在目标方法执行之后进行操作
后置通知:获得方法的返回值
/**
* 切面类
*
* @author Administrator
*
*/
public class MyAspectXML {
/**
* 后置通知
*/
public void writeLog(Object result) {
System.out.println("日志记录" + result);
}
}
修改DAO和DAOImpl中delete方法的返回值类型
@Override
public String delete() {
// TODO Auto-generated method stub
System.out.println("删除商品");
return "xxx";
}
<aop:config>
<!--表达式配置哪些类的哪些方法需要进行增强 -->
<aop:pointcut expression="execution(* spring.demo3.ProductDaoImpl.save(..))"
id="pointcut1" />
<aop:pointcut expression="execution(* spring.demo3.ProductDaoImpl.delete(..))"
id="pointcut2" />
<!--配置切面 -->
<aop:aspect ref="myAspect">
<!--前置通知 -->
<aop:before method="checkPri" pointcut-ref="pointcut1" />
<!--后置通知 -->
<aop:after-returning method="writeLog"
pointcut-ref="pointcut2" returning="result" /><!--名字要和方法中参数名字对上 --!>
</aop:aspect>
</aop:config>
3、环绕通知:在目标方法执行之前和之后进行操作
环绕通知可以阻止目标方法的执行。
package spring.demo3;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
/**
* 切面类
*
* @author Administrator
*
*/
public class MyAspectXML {
/**
* 性能监控(环绕通知)
*
* @throws Throwable
*/
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("环绕前通知=========");
Object obj = joinPoint.proceed();
System.out.println("环绕后通知=========");
return obj;
}
}
<!--通过AOP的配置完成对目标类产生代理 -->
<aop:config>
<!--表达式配置哪些类的哪些方法需要进行增强 -->
<aop:pointcut expression="execution(* spring.demo3.ProductDaoImpl.save(..))"
id="pointcut1" />
<aop:pointcut expression="execution(* spring.demo3.ProductDaoImpl.delete(..))"
id="pointcut2" />
<aop:pointcut expression="execution(* spring.demo3.ProductDaoImpl.update(..))"
id="pointcut3" />
<!--配置切面 -->
<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:config>
4、异常抛出通知:在程序出现异常的时候,进行的操作
package spring.demo3;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
/**
* 切面类
*
* @author Administrator
*
*/
public class MyAspectXML {
/**
* 异常抛出通知
*/
public void afterThrowing(Throwable ex) {
System.out.println("异常抛出通知========" + ex.getMessage());
}
}
@Override
public void find() {
// TODO Auto-generated method stub
System.out.println("查找商品");
// int i = 1 / 0;
}
<!--通过AOP的配置完成对目标类产生代理 -->
<aop:config>
<!--表达式配置哪些类的哪些方法需要进行增强 -->
<aop:pointcut expression="execution(* spring.demo3.ProductDaoImpl.save(..))"
id="pointcut1" />
<aop:pointcut expression="execution(* spring.demo3.ProductDaoImpl.delete(..))"
id="pointcut2" />
<aop:pointcut expression="execution(* spring.demo3.ProductDaoImpl.update(..))"
id="pointcut3" />
<aop:pointcut expression="execution(* spring.demo3.ProductDaoImpl.find(..))"
id="pointcut4" />
<!--配置切面 -->
<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:aspect>
</aop:config>
5、最终通知
package spring.demo3;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
/**
* 切面类
*
* @author Administrator
*
*/
public class MyAspectXML {
/**
* 最终通知:相当于finally代码块中的内容
*/
public void after() {
System.out.println("最终通知==========");
}
}
<!--通过AOP的配置完成对目标类产生代理 -->
<aop:config>
<!--表达式配置哪些类的哪些方法需要进行增强 -->
<aop:pointcut expression="execution(* spring.demo3.ProductDaoImpl.save(..))"
id="pointcut1" />
<aop:pointcut expression="execution(* spring.demo3.ProductDaoImpl.delete(..))"
id="pointcut2" />
<aop:pointcut expression="execution(* spring.demo3.ProductDaoImpl.update(..))"
id="pointcut3" />
<aop:pointcut expression="execution(* spring.demo3.ProductDaoImpl.find(..))"
id="pointcut4" />
<!--配置切面 -->
<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 method="after" pointcut-ref="pointcut4" />
</aop:aspect>
</aop:config>
6、引介通知(不用会)
7、Spring的切入点表达式写法
基于execution函数完成的
语法:
写法 [访问修饰符] 方法返回值 包名.类名.方法名(参数)
1、 可无 void spring.demo1.CustomerDao.save(..)
2、 * * . * . *Dao .save(..)
3、 * spring.demo1.CustomerDao+.save(..) //+号表示及其子类
4、 * spring.. * . *(..) //表示及其子包下的所有类的所有方法
5、 * spring.demo1.CustomerDao.save*(..)//表示以save开头的方法