Spring学习
Posted WBlog
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring学习相关的知识,希望对你有一定的参考价值。
一、Spring介绍
Spring负责管理项目中的所有对象。Spring是一站式框架,仅仅使用Spring足以完成Web项目。
二、Spring项目搭建以及测试
环境为IDEA,Spring项目创建:
创建一个对象:
public class User { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } }
配置文件(建议放到src下面):
<?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"> <!--将user对象交给Spring容器--> <bean id="user" class="domain.User"> <property name="name" value="helloworld"></property> </bean> </beans>
测试代码:
public class Main { public static void main(String[] args){ //创建容器对象 //ApplicationContext对象在加载 spring-config.xml(容器启动)时候就会创建 ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml"); //向容器要user对象 User user = (User) context.getBean("user"); System.out.println(user.getName()); } }
运行结果:helloworld
三、IOC和DI
IOC:Inverse Of Control反转控制。即将我们创建对象的方式反转,以前对象的创建和依赖关系是我们自己维护。使用Spring之后,对象的创建以及依赖关系可以由Spring完成创建以及注入。
DI:Dependency Injection依赖注入,实现IOC思想需要DI做支持,注入方式:set方式注入、构造方式注入、字段注入。注入类型:值类型注入、引用类型注入。
四、三种创建对象方式
4.1、空参构造方法创建
public class User { public User() { } private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } }
<?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"> <!--创建方式1:空参构造创建--> <bean name="user" class="domain.User"> <property name="name" value="naruto"></property> </bean> </beans>
4.2、静态工厂方法创建
public class UserFactory { public static User createUser(){ System.out.println("静态工厂创建User"); return new User(); } }
<?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"> <!-- 创建方式2:静态工厂创建 调用UserFactory的createUser方法创建名为user2的对象.放入容器 --> <bean name="user2" class="domain.UserFactory" factory-method="createUser" > </bean> </beans>
4.3、实例工厂方法创建
public class UserFactory { public User createUser2(){ System.out.println("实例工厂创建User"); return new User(); } }
<?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"> <!-- 创建方式3:实例工厂创建 调用UserFactory对象的createUser2方法创建名为user3的对象.放入容器 --> <bean name="user3" factory-bean="userFactory" factory-method="createUser2" > </bean> <bean name="userFactory" class="domain.UserFactory" > </bean> </beans>
五、Scpoe属性
Scpoe即Bean的作用范围:
singleton :默认值,单例的。
prototype :多例的
request :WEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 request 域中。
session :WEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 session 域中。(了解即可)
globalSession :WEB 项目中,应用在 Porlet 环境.如果没有 Porlet 环境那么 globalSession 相当于 session。(了解即可)
六、模块化配置
在一个配置文件中包含另一个配置文件:
<import resource="applicationContext2.xml"></import>
七、Spring属性注入
7.1、set方法注入
User对象:
public class User { public User() { } private String name; private Car car; public Car getCar() { return car; } public void setCar(Car car) { this.car = car; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "User{" + "name=‘" + name + ‘‘‘ + ", car=" + car + ‘}‘; } }
Car对象:
public class Car { private String color; public String getColor() { return color; } public void setColor(String color) { this.color = color; } @Override public String toString() { return "Car{" + "color=‘" + color + ‘‘‘ + ‘}‘; } }
注入容器:
<?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.spring framework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!--set方式注入--> <bean name="user6" class="domain.User"> <!--为User对象中名为name的属性注入naruto--> <property name="name" value="naruto"></property> <property name="car" ref="car"></property> </bean> <!--将car对象配置到容器中--> <bean name="car" class="domain.Car"> <!--为car对象中名为color的属性注入yellow--> <property name="color" value="yellow"></property> </bean> </beans>
运行测试:
public class Main { public static void main(String[] args){ ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml"); User user = (User) context.getBean("user6"); System.out.println(user.toString()); } }
运行结果:User{name=’naruto’, car=Car{color=’yellow’}}
7.2、构造函数注入
User对象:
public class User { public User() { } private String name; private Car car; public User(Car car, String name) { this.car = car; this.name = name; } public Car getCar() { return car; } public void setCar(Car car) { this.car = car; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "User{" + "name=‘" + name + ‘‘‘ + ", car=" + car + ‘}‘; } }!
配置:
<?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"> <!--将car对象配置到容器中--> <bean name="car" class="domain.Car"> <!--为car对象中名为color的属性注入yellow--> <property name="color" value="yellow"></property> </bean> <!--构造方法注入--> <bean name="user66" class="domain.User"> <!--为User对象中名为name的属性注入naruto--> <constructor-arg name="name" value="long" index="1"></constructor-arg> <constructor-arg name="car" ref="car"></constructor-arg> </bean> </beans>
运行测试:
public class Main { public static void main(String[] args){ ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml"); User user = (User) context.getBean("user66"); System.out.println(user.toString()); } }
7.3、复杂类型注入
创建一个复杂对象:
public class CollectionBean { private Object[] arr;//数组类型注入 private List list;//list/set 类型注入 private Map map;//map类型注入 private Properties prop;//properties类型注入 public Object[] getArr() { return arr; } public void setArr(Object[] arr) { this.arr = arr; } public List getList() { return list; } public void setList(List list) { this.list = list; } public Map getMap() { return map; } public void setMap(Map map) { this.map = map; } public Properties getProp() { return prop; } public void setProp(Properties prop) { this.prop = prop; } @Override public String toString() { return "CollectionBean [arr=" + Arrays.toString(arr) + ", list=" + list + ", map=" + map + ", prop=" + prop + "]"; } }
配置:
<!-- ============================================================= --> <!-- 复杂类型注入 --> <bean name="cb" class="domain.CollectionBean" > <!-- 如果数组中只准备注入一个值(对象),直接使用value|ref即可 <property name="arr" value="tom" ></property> --> <!-- array注入,多个元素注入 --> <property name="arr"> <array> <value>tom</value> <value>jerry</value> <ref bean="user6" /> </array> </property> <!-- 如果List中只准备注入一个值(对象),直接使用value|ref即可 <property name="list" value="jack" ></property>--> <property name="list" > <list> <value>jack</value> <value>rose</value> <ref bean="user3" /> </list> </property> <!-- map类型注入 --> <property name="map" > <map> <entry key="url" value="jdbc:mysql:///crm" ></entry> <entry key="user" value-ref="user6" ></entry> <entry key-ref="user3" value-ref="user2" ></entry> </map> </property> <!-- prperties 类型注入 --> <property name="prop" > <props> <prop key="driverClass">com.jdbc.mysql.Driver</prop> <prop key="userName">root</prop> <prop key="password">1234</prop> </props> </property> </bean>
八、Spring整合Web项目配置
配置监听器:
<!-监听器:服务器启动,Spring对象创建,服务器销毁,Spring对象销毁。--> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listenerclass> </listener> <!--指定加载Spring配置文件的位置。--> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml</param-value> </context-param>
Spring将容器放入ServletContext中去了,即可以从Application域中获取Spring对象。
九、使用注解配置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" 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"> <!--扫描指定包及其子包下所有类中的注解--> <context:component-scan base-package="domain"></context:component-scan> </beans>
将car对象注入到Spring中:
@Component("car2") public class Car { @Value("yellow") private String color; public void setColor(String color) { this.color = color; } @Override public String toString() { return "Car{" + "color=‘" + color + ‘‘‘ + ‘}‘; } }
将user对象注入到Spring中:
//对象名为user,将其放入Spring容器中 @Component("user") //@Service("user") //service层 //@Controller("user") //web层 //@Repository("user") //dao层 //@Scope(scopeName = "protoType") //指定对象作用范围:多例 public class User { @Value("long") //通过反射的Field赋值,破坏了封装性 private String name; //@Autowired //自动装配 //自动装配问题:如果匹配多个类型一致的对象,将无法选择具体注入哪一个对象 //@Qualifier("car2") 使用Qualifier注解告诉Spring容器自动装配哪个名称的对象 //使用Resource注解手动注入,指定注入哪个名称的对象(推荐) @Resource(name="car2") private Car car; @PostConstruct //在对象被创建后调用 public void init(){ System.out.println("我是初始化方法"); } @PreDestroy //在对象被销毁之前调用 public void destroy(){ System.out.println("我是销毁方法"); } public Car getCar() { return car; } public void setCar(Car car) { this.car = car; } public String getName() { return name; } //@Value("long") //通过set方法赋值(推荐) public void setName(String name) { this.name = name; } @Override public String toString() { return "User{" + "name=‘" + name + ‘‘‘ + ", car=" + car + ‘}‘; } }
测试:
public class Main { public static void main(String[] args){ ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml"); User bean = (User) context.getBean("user"); System.out.println(bean.toString()); } }
运行结果:
我是初始化方法
User{name=’long’, car=Car{color=’yellow’}}
十、Spring中的AOP
Spring能够为容器中管理的对象生成动态代理对象。
Spring实现Aop的原理:Spring封装了动态代理和cglib代理
动态代理局限性:被代理对象必须要有接口,才能产生代理对象。
cglib代理:第三方代理技术,可以对任何类生成代理。代理的原理是对目标对象进行继承代理。如果目标对象被final修饰,那么该类无法被cglib代理。
使用动态代理:
UserService:
public interface UserService { void save(); void delete(); void update(); void find(); }
UserServiceImpl:
public class UserServiceImpl implements UserService { @Override public void save() { System.out.println("保存用户!"); //int i = 1/0; } @Override public void delete() { System.out.println("删除用户!"); } @Override public void update() { System.out.println("更新用户!"); } @Override public void find() { System.out.println("查找用户!"); } }
生成代理:
public class UserServiceProxyFactory implements InvocationHandler { public UserServiceProxyFactory(UserService us) { super(); this.us = us; } private UserService us; public UserService getUserServiceProxy(){ //生成动态代理 UserService usProxy = (UserService) Proxy.newProxyInstance(UserServiceProxyFactory.class.getClassLoader(), UserServiceImpl.class.getInterfaces(), //被代理对象实现的接口 this); //返回 return usProxy; } @Override public Object invoke(Object arg0, Method method, Object[] arg2) throws Throwable { System.out.println("打开事务!"); Object invoke = method.invoke(us, arg2); System.out.println("提交事务!"); return invoke; } }
测试:
public class Demo { @Test //动态代理 public void fun1(){ UserService us = new UserServiceImpl(); //传入代理对象 UserServiceProxyFactory factory = new UserServiceProxyFactory(us); UserService usProxy = factory.getUserServiceProxy(); usProxy.save(); //代理对象与被代理对象实现了相同的接口 //代理对象 与 被代理对象没有继承关系 System.out.println(usProxy instanceof UserServiceImpl );//false } }
运行结果:
打开事务!
保存用户!
提交事务!
false
2、动态代理
什么是代理(中介)
目标对象/被代理对象 —— 房主:真正的租房的方法
代理对象 ——- 黑中介:有租房子的方法(调用房主的租房的方法)
执行代理对象方法的对象 —- 租房的人
流程:我们要租房—–>中介(租房的方法)——>房主(租房的方法)
抽象:调用对象—–>代理对象——>目标对象
动态代理的API:
在jdk的API中存在一个Proxy中存在一个生成动态代理的的方法newProxyInstance:
newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
返回值:Object就是代理对象
参数:loader:代表与目标对象相同的类加载器—–目标对象.getClass().getClassLoader()
interfaces:代表与目标对象实现的所有的接口字节码对象数组
h:具体的代理的操作,InvocationHandler接口
栗子1:
目标对象接口:
public interface TargetInterface { public void method1(); public String method2(); public int method3(int x); }
目标对象:
//目标对象 public class Target implements TargetInterface{ @Override public void method1() { System.out.println("method1 running..."); } @Override public String method2() { System.out.println("method2 running..."); return "method2"; } @Override public int method3(int x) { return x; } }
代理对象:
//代理对象 public class ProxyTest { @Test public void test1(){ //获得动态的代理对象----在运行时 在内存中动态的为Target创建一个虚拟的代理对象 //objProxy是代理对象 根据参数确定到底是谁的代理对象 TargetInterface objProxy = (TargetInterface) Proxy.newProxyInstance( Target.class.getClassLoader(), //与目标对象相同的类加载器 new Class[]{TargetInterface.class}, new InvocationHandler() { //invoke 代表的是执行代理对象的方法 @Override //method:代表目标对象的方法字节码对象 //args:代表目标对象的响应的方法的参数 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("目标方法前的逻辑"); //执行目标对象的方法 Object invoke = method.invoke(new Target(), args); System.out.println("目标方法后的逻辑"); return invoke; } } ); objProxy.method1(); String method2 = objProxy.method2(); //System.out.println(method2); } }
运行结果:
目标方法前的逻辑
method1 running…
目标方法后的逻辑
目标方法前的逻辑
method2 running…
目标方法后的逻辑
栗子2:
package com.proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class ProxyTest2 { public static void main(String[] args) { final Target target = new Target(); //动态创建代理对象 TargetInterface proxy = (TargetInterface) Proxy.newProxyInstance( target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() { @Override //被执行几次?------- 看代理对象调用方法几次 //代理对象调用接口相应方法 都是调用invoke /* * proxy:是代理对象 * method:代表的是目标方法的字节码对象 * args:代表是调用目标方法时参数 */ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //反射知识点 Object invoke = method.invoke(target, args);//目标对象的相应方法 //retrun返回的值给代理对象 return invoke; } } ); //调用invoke---Method:目标对象的method1方法 args:null 返回值null proxy.method1(); //调用invoke---Method:目标对象的method2方法 args:null 返回值method2 String method2 = proxy.method2(); ////调用invoke-----Method:目标对象的method3方法 args:Object[]{100} 返回值100 int method3 = proxy.method3(100); System.out.println(method2); System.out.println(method3); } }
运行结果:
method1 running…
method2 running…
method2
100
栗子3:全局编码
public class EncodingFilter implements Filter{ @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { final HttpServletRequest req = (HttpServletRequest) request; //使用动态代理完成全局编码 HttpServletRequest enhanceRequset = (HttpServletRequest) Proxy.newProxyInstance( req.getClass().getClassLoader(), req.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //对getParameter方法进行增强 String name = method.getName();//获得目标对象的方法名称 if("getParameter".equals(name)){ String invoke = (String) method.invoke(req, args);//乱码 //转码 invoke = new String(invoke.getBytes("iso8859-1"),"UTF-8"); return invoke; } return method.invoke(req, args); } } ); chain.doFilter(enhanceRequset, response); } @Override public void destroy() { } @Override public void init(FilterConfig filterConfig) throws ServletException { } }
使用cglib代理:
public class UserServiceProxyFactory2 implements MethodInterceptor { public UserService getUserServiceProxy(){ //帮我们生成代理对象 Enhancer en = new Enhancer(); //设置对谁进行代理 en.setSuperclass(UserServiceImpl.class); //代理要做什么 en.setCallback(this); //创建代理对象 UserService us = (UserService) en.create(); return us; } @Override public Object intercept(Object prxoyobj, Method method, Object[] arg, MethodProxy methodProxy) throws Throwable { //打开事务 System.out.println("打开事务!"); //调用原有方法 Object returnValue = methodProxy.invokeSuper(prxoyobj, arg); //提交事务 System.out.println("提交事务!"); return returnValue; } }
代码测试:
@Test public void fun2(){ UserServiceProxyFactory2 factory = new UserServiceProxyFactory2(); UserService usProxy = factory.getUserServiceProxy(); usProxy.save(); //判断代理对象是否属于被代理对象类型 //代理对象继承了被代理对象=>true System.out.println(usProxy instanceof UserServiceImpl ); //true }
运行结果:
打开事务!
保存用户!
提交事务!
true
2.2、Spring中的AOP名词解释
JoinPoint(连接点):目标对象中所有可以增强的方法。
PointCut(切入点):目标对象,已经增强的方法。
Advice(通知 / 增强):增强的代码。
Target(目标对象):被代理对象。
Weaving(织入):将通知应用到切入点的过程。
Proxy(代理):将通知织入到目标对象之后形成代理对象。
Aspect(切面):切入点+通知
2.3、Spring中的AOP演示
创建通知类:
//表示该类是一个通知类 public class MyAdvice { //前置通知 public void before(){ System.out.println("这是前置通知!!"); } //后置通知 public void afterReturning(){ System.out.println("这是后置通知(如果出现异常不会调用)!!"); } //环绕通知 public Object around(ProceedingJoinPoint pjp) throws Throwable { System.out.println("这是环绕通知之前的部分!!"); Object proceed = pjp.proceed();//调用目标方法 System.out.println("这是环绕通知之后的部分!!"); return proceed; } //异常通知 public void afterException(){ System.out.println("出事啦!出现异常了!!"); } //后置通知 public void after(){ System.out.println("这是后置通知(出现异常也会调用)!!"); } }
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" 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"> <!--扫描指定包及其子包下所有类中的注解--> <context:component-scan base-package="domain"></context:component-scan> <context:component-scan base-package="aop"></context:component-scan> <!--1、配置目标对象,得到的是代理的对象--> <bean name="userServiceTarget" class="aop.service.UserServiceImpl"></bean> <!--2、配置通知对象--> <bean name="myAdvice" class="aop.advice.MyAdvice"></bean> <!--3、配置将通知织入目标对象--> <aop:config> <!-- 配置切入点 public void cn.long.service.UserServiceImpl.save() void cn.long.service.UserServiceImpl.save() * cn.long.service.UserServiceImpl.save() * cn.long.service.UserServiceImpl.*() * cn.long.service.*ServiceImpl.*(..) * cn.long.service..*ServiceImpl.*(..) --> <aop:pointcut id="pc" expression="execution(* aop.service.UserServiceImpl.*(..))"></aop:pointcut> <aop:aspect ref="myAdvice" > <!-- 指定名为before方法作为前置通知 --> <aop:before method="before" pointcut-ref="pc"></aop:before> <!-- 后置 --> <aop:after-returning method="afterReturning" pointcut-ref="pc"></aop:after-returning> <!-- 环绕通知 --> <aop:around method="around" pointcut-ref="pc" /> <!-- 异常拦截通知 --> <aop:after-throwing method="afterException" pointcut-ref="pc"></aop:after-throwing> <!-- 后置 --> <aop:after method="after" pointcut-ref="pc"></aop:after> </aop:aspect> </aop:config> </beans>
测试:
package aop; import aop.service.UserService; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import javax.annotation.Resource; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:/spring-config.xml") public class Demo { @Resource(name="userServiceTarget") private UserService us; @Test public void fun1(){ us.save(); } }
运行结果:
我是初始化方法这是前置通知!!
这是环绕通知之前的部分!!
保存用户!
这是后置通知(出现异常也会调用)!!
这是环绕通知之后的部分!!
这是后置通知(如果出现异常不会调用)!!
我是销毁方法
2.3、Spring中的AOP注解方式演示
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" 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"> <!--1、配置目标对象--> <bean name="userServiceTarget" class="aop.service.UserServiceImpl"></bean> <!--2、配置通知对象--> <bean name="myAdvice" class="aop.advice.MyAdvice2"></bean> <!-- 3.开启使用注解完成织入 --> <aop:aspectj-autoproxy></aop:aspectj-autoproxy> </beans>
通知:
package aop.advice; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; //通知类 @Aspect //表示该类是一个通知类 public class MyAdvice2 { @Pointcut("execution(* aop.service.UserServiceImpl.*(..))") public void pc(){} //前置通知 //指定该方法是前置通知,并制定切入点 @Before("MyAdvice2.pc()") public void before(){ System.out.println("这是前置通知!!"); } //后置通知 @AfterReturning("execution(* aop.service.UserServiceImpl.*(..))") public void afterReturning(){ System.out.println("这是后置通知(如果出现异常不会调用)!!"); } //环绕通知 @Around("execution(* aop.service.UserServiceImpl.*(..))") public Object around(ProceedingJoinPoint pjp) throws Throwable { System.out.println("这是环绕通知之前的部分!!"); Object proceed = pjp.proceed();//调用目标方法 System.out.println("这是环绕通知之后的部分!!"); return proceed; } //异常通知 @AfterThrowing("execution(* aop.service.UserServiceImpl.*(..))") public void afterException(){ System.out.println("出事啦!出现异常了!!"); } //后置通知 @After("execution(* aop.service.UserServiceImpl.*(..))") public void after(){ System.out.println("这是后置通知(出现异常也会调用)!!"); } }
测试:
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:/aop/advice/spring-config.xml") public class Demo { @Resource(name="userServiceTarget") private UserService us; @Test public void fun1(){ us.save(); } }
运行结果:
这是环绕通知之前的部分!!
这是前置通知!!
保存用户!
这是环绕通知之后的部分!!
这是后置通知(出现异常也会调用)!!
这是后置通知(如果出现异常不会调用)!!
十一、JDBC模板对象
Spring整合了一个可以操作数据库的对象JDBCTemplate(JBDC模板对象),该对象封装了jdbc技术,与DBUtils中的QueryRunner非常相似。
演示一下JDBCTemplate的使用:
//演示JDBC模板 public class Demo { @Test public void fun1() throws Exception { //0 准备连接池 ComboPooledDataSource dataSource = new ComboPooledDataSource(); dataSource.setDriverClass("com.mysql.jdbc.Driver"); dataSource.setJdbcUrl("jdbc:mysql:///test"); dataSource.setUser("root"); dataSource.setPassword("666"); //1 创建JDBC模板对象 JdbcTemplate jt = new JdbcTemplate(); jt.setDataSource(dataSource); //2 书写sql,并执行 String sql = "insert into user values(null,‘long‘) "; jt.update(sql); } }
运行结果:
JDBCTemplate增删改查:
UserDao :
public interface UserDao { //增 void save(User u); //删 void delete(Integer id); //改 void update(User u); //查 User getById(Integer id); //查 int getTotalCount(); //查 List<User> getAll(); }
UserDaoImpl:
//使用JDBC模板实现增删改查 public class UserDaoImpl extends JdbcDaoSupport implements UserDao { @Override public void save(User u) { String sql = "insert into t_user values(null,?) "; super.getJdbcTemplate().update(sql, u.getName()); } @Override public void delete(Integer id) { String sql = "delete from t_user where id = ? "; super.getJdbcTemplate().update(sql,id); } @Override public void update(User u) { String sql = "update t_user set name = ? where id=? "; super.getJdbcTemplate().update(sql, u.getName(),u.getId()); } @Override public User getById(Integer id) { String sql = "select * from t_user where id = ? "; return super.getJdbcTemplate().queryForObject(sql,new RowMapper<User>(){ @Override public User mapRow(ResultSet rs, int arg1) throws SQLException { User u = new User(); u.setId(rs.getInt("id")); u.setName(rs.getString("name")); return u; }}, id); } @Override public int getTotalCount() { String sql = "select count(*) from t_user "; Integer count = super.getJdbcTemplate().queryForObject(sql, Integer.class); return count; } @Override public List<User> getAll() { String sql = "select * from t_user "; List<User> list = super.getJdbcTemplate().query(sql, new RowMapper<User>(){ @Override public User mapRow(ResultSet rs, int arg1) throws SQLException { User u = new User(); u.setId(rs.getInt("id")); u.setName(rs.getString("name")); return u; }}); return list; } }
将其配置到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"> <!-- 指定spring读取db.properties配置 --> <context:property-placeholder location="classpath:db.properties" /> <!-- 1.将连接池放入spring容器 --> <bean name="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" > <property name="jdbcUrl" value="${jdbc.jdbcUrl}" ></property> <property name="driverClass" value="${jdbc.driverClass}" ></property> <property name="user" value="${jdbc.user}" ></property> <property name="password" value="${jdbc.password}" ></property> </bean> <!-- 2.将JDBCTemplate放入spring容器 --> <bean name="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate" > <property name="dataSource" ref="dataSource" ></property> </bean> <!-- 3.将UserDao放入spring容器 --> <bean name="userDao" class="jdbcTemplate.UserDaoImpl" > <!-- <property name="jt" ref="jdbcTemplate" ></property> --> <property name="dataSource" ref="dataSource" ></property> </bean> </beans>
测试:
package jdbcTemplate; import com.mchange.v2.c3p0.ComboPooledDataSource; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import javax.annotation.Resource; /** * Created by KPL on 2018/7/1. */ //演示JDBC模板 @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:spring-config.xml") public class Demo { @Resource(name="userDao") private UserDao ud; @Test public void fun2() throws Exception{ User u = new User(); u.setName("naruto"); ud.save(u); } @Test public void fun3() throws Exception{ User u = new User(); u.setId(2); u.setName("jack"); ud.update(u); } @Test public void fun4() throws Exception{ ud.delete(2); } @Test public void fun5() throws Exception{ System.out.println(ud.getTotalCount()); } @Test public void fun6() throws Exception{ System.out.println(ud.getById(1)); } @Test public void fun7() throws Exception{ System.out.println(ud.getAll()); } }
运行结果:
public class UserDaoImpl extends JdbcDaoSupport继承了JdbcDaoSupport,Spring会根据连接池自动创建JDBC模板对象。也就是说不需要手动擦行间JBDC模板对象,从父类方法中直接获取即可。
String sql = "insert into t_user values(null,?) "; super.getJdbcTemplate().update(sql, u.getName());
此时,配置文件修改成如下:
<?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"> <!-- 指定spring读取db.properties配置 --> <context:property-placeholder location="classpath:db.properties" /> <!-- 1.将连接池放入spring容器 --> <bean name="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" > <property name="jdbcUrl" value="${jdbc.jdbcUrl}" ></property> <property name="driverClass" value="${jdbc.driverClass}" ></property> <property name="user" value="${jdbc.user}" ></property> <property name="password" value="${jdbc.password}" ></property> </bean> <!-- 3.将UserDao放入spring容器 --> <bean name="userDao" class="jdbcTemplate.UserDaoImpl" > <property name="dataSource" ref="dataSource" ></property> </bean> </beans>
十二、Spring事务管理
1.1、事务概述
(1)什么是事务?
一件事情有n个组成单元 要不这n个组成单元同时成功,要不n个单元就同时失败
就将n个组成单元放到一个事务中。
(2)mysql的事务
默认的事务:一条sql语句就是一个事务 默认就开启事务并提交事务。sql执行完了之后自动提交了。
手动事务:
显示的开启一个事务:start transaction。
事务提交:commit代表从开启事务到事务提交,中间的所有的sql都认为有效真正的更新数据库。
事务的回滚:rollback代表事务的回滚从开启事务到事务回滚中间的所有的sql操作都认为无效数据库没有被更新。
1.2、JDBC事务操作
默认是自动事务:
执行sql语句:executeUpdate() —- 每执行一次executeUpdate方法代表事务自动提交。
通过jdbc的API手动事务:
开启事务:conn.setAutoComnmit(false);
提交事务:conn.commit();
回滚事务:conn.rollback();
注意:控制事务的connnection必须是同一个执行sql的connection与开启事务的connnection必须是同一个才能对事务进行控制。
1.3、DBUtils事务操作
QueryRunner
有参构造:QueryRunner runner = new QueryRunner(DataSource dataSource);
有参构造将数据源(连接池)作为参数传入QueryRunner,QueryRunner会从连接池中获得一个数据库连接资源操作数据库,所以直接使用无Connection参数的update方法即可操作数据库。不需要事务则采用此方法。
无参构造:QueryRunner runner = new QueryRunner();
无参的构造没有将数据源(连接池)作为参数传入QueryRunner,那么我们在使用QueryRunner对象操作数据库时要使用有Connection参数的方法。此方法能实现事务。
1.4、事务的特性ACID
(1)原子性(Atomicity)原子性是指事务是一个不可分割的工作单位,事务中的操作 要么都发生,要么都不发生。
(2)一致性(Consistency)一个事务中,事务前后数据的完整性必须保持一致。
(3)隔离性(Isolation)多个事务,事务的隔离性是指多个用户并发访问数据库时,一个用户的 事务不能被其它用户的事务所干扰,多个并发事务之间数据要相互隔离。
(4)持久性(Durability)持久性是指一个事务一旦被提交,它对数据库中数据的改变 就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响。
五、事务的隔离
如果不考虑隔离性,事务存在3种并发访问问题。
脏读:B事务读取到了A事务尚未提交的数据 —— 要求B事务要读取A事务提交的数据
不可重复读:一个事务中 两次读取的数据的内容不一致 —– 要求的是一个事务中多次读取时数据是一致的 — unpdate
幻读/虚读:一个事务中 两次读取的数据的数量不一致 —– 要求在一个事务多 次读取的数据的数量是一致的 –insert delete
事务的隔离级别:
(1)read uncommitted : 读取尚未提交的数据 :哪个问题都不能解决
(2)read committed:读取已经提交的数据 :可以解决脏读 —- oracle默认的
(3)repeatable read:重读读取:可以解决脏读和不可重复读 —mysql默认的
(4)serializable:串行化:可以解决脏读不可重复读和虚读—相当于锁表
注意:mysql数据库默认的隔离级别
查看mysql数据库默认的隔离级别:select @@tx_isolation
设置mysql的隔离级别:set session transaction isolation level 设置事务隔离级别
因为在不用平台,操纵事务的代码各不相同,于是Spring提供了一个接口PlatfromTransactionManager接口,针对不同的平台,提供不同的实现类。
***** 真正管理事务的对象 org.springframework.jdbc.datasource.DataSourceTransactionManager //使用 SpringJDBC 或 iBatis 进行持久化数据时使用 org.springframework.orm.hibernate3.HibernateTransactionManager //使用Hibernate 版本进行持久化数据时使用
事务的传播行为:决定业务方法之间调用,事务应该如何处理。
//保证同一个事务中
PROPAGATION_REQUIRED 支持当前事务,如果不存在 就新建一个(默认)
PROPAGATION_SUPPORTS 支持当前事务,如果不存在,就不使用事务
PROPAGATION_MANDATORY 支持当前事务,如果不存在,抛出异常
//保证没有在同一个事务中
PROPAGATION_REQUIRES_NEW 如果有事务存在,挂起当前事务,创建一个新的事务
PROPAGATION_NOT_SUPPORTED 以非事务方式运行,如果有事务存在,挂起当前事务
PROPAGATION_NEVER 以非事务方式运行,如果有事务存在,抛出异常
PROPAGATION_NESTED 如果当前事务存在,则嵌套事务执行
演示一下Spring事务:
先完成基本的转账功能:
Dao:
package Transaction.dao; public interface AccountDao { //加钱 void increaseMoney(Integer id, Double money); //减钱 void decreaseMoney(Integer id, Double money); }
DaoImpl:
public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao { @Override public void increaseMoney(Integer id, Double money) { getJdbcTemplate().update("update t_account set money = money+? where id = ? ", money,id); } @Override public void decreaseMoney(Integer id, Double money) { getJdbcTemplate().update("update t_account set money = money-? where id = ? ", money,id); } }
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" 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"> <!-- 指定spring读取db.properties配置 --> <context:property-placeholder location="classpath:db.properties" /> <!-- 1.将连接池 --> <bean name="dataSource1" class="com.mchange.v2.c3p0.ComboPooledDataSource" > <property name="jdbcUrl" value="${jdbc.jdbcUrl}" ></property> <property name="driverClass" value="${jdbc.driverClass}" ></property> <property name="user" value="${jdbc.user}" ></property> <property name="password" value="${jdbc.password}" ></property> </bean> <!-- 2.Dao--> <bean name="accountDao" class="Transaction.dao.AccountDaoImpl" > <property name="dataSource" ref="dataSource1" ></property> </bean> <!-- 3.Service--> <bean name="accountService" class="Transaction.service.AccountServiceImpl" > <property name="ad" ref="accountDao" ></property> </bean> </beans>
测试类:
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:spring-config2.xml") public class Demo { @Resource(name="accountService") private AccountService as; @Test public void fun1(){ as.transfer(1, 2, 100d); } }
运行结果:
现在开始加事务:
Spring管理事务的方式:
编码式:(了解)
将核心事务管理器配置到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"> <!-- 指定spring读取db.properties配置 --> <context:property-placeholder location="classpath:db.properties" /> <!-- 事务核心管理器,封装了所有事务操作. 依赖于连接池 --> <bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager" > <property name="dataSource" ref="dataSource1" ></property> </bean> <!-- 事务模板对象 --> <bean name="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate" > <property name="transactionManager" ref="transactionManager" ></property> </bean> <!-- 1.将连接池 --> <bean name="dataSource1" class="com.mchange.v2.c3p0.ComboPooledDataSource" > <property name="jdbcUrl" value="${jdbc.jdbcUrl}" ></property> <property name="driverClass" value="${jdbc.driverClass}" ></property> <property name="user" value="${jdbc.user}" ></property> <property name="password" value="${jdbc.password}" ></property> </bean> <!-- 2.Dao--> <bean name="accountDao" class="Transaction.dao.AccountDaoImpl" > <property name="dataSource" ref="dataSource1" ></property> </bean> <!-- 3.Service--> <bean name="accountService" class="Transaction.service.AccountServiceImpl" > <property name="ad" ref="accountDao" ></property> <property name="tt" ref="transactionTemplate" ></property> </bean> </beans>
ServiceImpl:
@Transactional(isolation=Isolation.REPEATABLE_READ,propagation=Propagation.REQUIRED,readOnly=true) public class AccountServiceImpl implements AccountService { private AccountDao ad ; private TransactionTemplate tt; @Override public void transfer(final Integer from,final Integer to,final Double money) { tt.execute(new TransactionCallbackWithoutResult() { @Override protected void doInTransactionWithoutResult(TransactionStatus arg0) { //减钱 ad.decreaseMoney(from, money); int i = 1/0; //加钱 ad.increaseMoney(to, money); } }); } public void setAd(AccountDao ad) { this.ad = ad; } public void setTt(TransactionTemplate tt) { this.tt = tt; } }
测试代码:
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:spring-config2.xml") public class Demo { @Resource(name="accountService") private AccountService as; @Test public void fun1(){ as.transfer(1, 2, 100d); } }
运行结果,由于加入了事务,报错之后,钱并不会转过去:
XML配置(aop)
Spring内部已经封装了事务通知方法,那么咱们只需要将它配置到业务处理上去就可以了。
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.2.xsd "> <!-- 指定spring读取db.properties配置 --> <context:property-placeholder location="classpath:db.properties" /> <!-- 事务核心管理器,封装了所有事务操作. 依赖于连接池 --> <bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager" > <property name="dataSource" ref="dataSource" ></property> </bean> <!-- 事务模板对象 --> <bean name="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate" > <property name="transactionManager" ref="transactionManager" ></property> </bean> <!-- 配置事务通知 --> <tx:advice id="txAdvice" transaction-manager="transactionManager" > <tx:attributes> <!-- 以方法为单位,指定方法应用什么事务属性 isolation:隔离级别 propagation:传播行为 read-only:是否只读 --> <tx:method name="save*" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false" /> <tx:method name="persist*" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false" /> <tx:method name="update*" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false" /> <tx:method name="modify*" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false" /> <tx:method name="delete*" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false" /> <tx:method name="remove*" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false" /> <tx:method name="get*" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="true" /> <tx:method name="find*" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="true" /> <tx:method name="transfer" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false" /> </tx:attributes> </tx:advice> <!-- 配置织入 --> <aop:config > <!-- 配置切点表达式 --> <aop:pointcut expression="execution(* Transaction.service.*ServiceImpl.*(..))" id="txPc"/> <!-- 配置切面 : 通知+切点 advice-ref:通知的名称 pointcut-ref:切点的名称 --> <aop:advisor advice-ref="txAdvice" pointcut-ref="txPc" /> </aop:config> <!-- 1.将连接池 --> <bean name="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" > <property name="jdbcUrl" value="${jdbc.jdbcUrl}" ></property> <property name="driverClass" value="${jdbc.driverClass}" ></property> <property name="user" value="${jdbc.user}" ></property> <property name="password" value="${jdbc.password}" ></property> </bean> <!-- 2.Dao--> <bean name="accountDao" class="Transaction.dao.AccountDaoImpl" > <property name="dataSource" ref="dataSource" ></property> </bean> <!-- 3.Service--> <bean name="accountService" class="Transaction.service.AccountServiceImpl" > <property name="ad" ref="accountDao" ></property> <property name="tt" ref="transactionTemplate" ></property> </bean> </beans>
注解配置(aop)
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.2.xsd "> <!-- 指定spring读取db.properties配置 --> <context:property-placeholder location="classpath:db.properties" /> <!-- 事务核心管理器,封装了所有事务操作. 依赖于连接池 --> <bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager" > <property name="dataSource" ref="dataSource" ></property> </bean> <!-- 事务模板对象 --> <bean name="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate" > <property name="transactionManager" ref="transactionManager" ></property> </bean> <!-- 开启使用注解管理aop事务 --> <tx:annotation-driven/> <!-- 1.将连接池 --> <bean name="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" > <property name="jdbcUrl" value="${jdbc.jdbcUrl}" ></property> <property name="driverClass" value="${jdbc.driverClass}" ></property> <property name="user" value="${jdbc.user}" ></property> <property name="password" value="${jdbc.password}" ></property> </bean> <!-- 2.Dao--> <bean name="accountDao" class="Transaction.dao.AccountDaoImpl" > <property name="dataSource" ref="dataSource" ></property> </bean> <!-- 3.Service--> <bean name="accountService" class="Transaction.service.AccountServiceImpl" > <property name="ad" ref="accountDao" ></property> <property name="tt" ref="transactionTemplate" ></property> </bean> </beans>
加入注解:
@Transactional(isolation= Isolation.REPEATABLE_READ,propagation= Propagation.REQUIRED,readOnly=true) public class AccountServiceImpl implements AccountService { private AccountDao ad ; private TransactionTemplate tt; @Override @Transactional(isolation=Isolation.REPEATABLE_READ,propagation=Propagation.REQUIRED,readOnly=false) public void transfer(final Integer from,final Integer to,final Double money) { //减钱 ad.decreaseMoney(from, money); int i = 1/0; //加钱 ad.increaseMoney(to, money); } public void setAd(AccountDao ad) { this.ad = ad; } public void setTt(TransactionTemplate tt) { this.tt = tt; } }
测试
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:spring-config4.xml") public class Demo { @Resource(name="accountService") private AccountService as; @Test public void fun1(){ as.transfer(1, 2, 100d); } }
运行结果,由于出错,钱不会转过去:
以上是关于Spring学习的主要内容,如果未能解决你的问题,请参考以下文章
Spring boot:thymeleaf 没有正确渲染片段
What's the difference between @Component, @Repository & @Service annotations in Spring?(代码片段
spring练习,在Eclipse搭建的Spring开发环境中,使用set注入方式,实现对象的依赖关系,通过ClassPathXmlApplicationContext实体类获取Bean对象(代码片段