文章目录
bean实例化方式
- bean的实例化方式有3种:默认构造、静态工厂、实例工厂
- 默认构造
<bean id="" class=""> 必须提供默认构造
- 静态工厂
常用与spring整合其他框架(工具)
静态工厂:用于生成实例对象,所有的方法必须是static
<bean id="" class="工厂全限定类名" factory-method="静态方法">
<bean id="userServiceId" class="static_factory.MyBeanFactory" factory-method="createService"/>
- 实例工厂:必须先有工厂实例对象,通过实例对象创建对象。提供所有的方法都是“非静态”的。
<!-- 创建工厂实例 -->
<bean id="myBeanFactoryId" class="factory.MyBeanFactory"></bean>
<!-- 获得userservice
* factory-bean 确定工厂实例
* factory-method 确定普通方法
-->
<bean id="userServiceId" factory-bean="myBeanFactoryId" factory-method="createService"></bean>
bean的种类
- 普通bean:
<bean id="" class="A">
,spring直接创建A实例,并返回 - FactoryBean:是一个特殊的bean,具有工厂生成对象能力,只能生成特定的对象。
- bean必须使用 FactoryBean接口,此接口提供方法 getObject() 用于获得特定bean。
<bean id="" class="FB">
先创建FB实例,调用getObject()方法,并返回方法的返回值。
FB fb = new FB(); return fb.getObject();
- BeanFactory 和 FactoryBean 对比
- BeanFactory:工厂,用于生成任意bean。
- FactoryBean:特殊bean,用于生成另一个特定的bean。
- 例如:ProxyFactoryBean ,此工厂bean用于生产代理。
<bean id="" class="....ProxyFactoryBean">
获得代理对象实例。AOP使用
生命周期
初始化和销毁
<bean id="" class="" init-method="初始化方法名称" destroy-method="销毁的方法名称">
BeanPostProcessor
- spring 提供一种机制,只要实现此接口BeanPostProcessor,并将实现类提供给spring容器,spring容器将自动地,在初始化方法前执行
Object postProcessBeforeInitialization(Object var1, String var2)
,在初始化方法后执行Object postProcessAfterInitialization(Object var1, String var2)
。 - 最后还需要将该类在spring的xml中进行配置。配置方法:
<bean class="">
属性依赖注入
- 分为基于xml的属性装配方式和基于注解的方式。
- 基于xml的装配又有通过构造方法和通过setter方法的方式。
构造方法
- 目标类
package constructor;
public class User {
private Integer uid;
private String username;
private Integer age;
public User(Integer uid, String username) {
super();
this.uid = uid;
this.username = username;
}
public User(String username, Integer age) {
super();
this.username = username;
this.age = age;
}
public Integer getUid() {
return uid;
}
public void setUid(Integer uid) {
this.uid = uid;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "User [uid=" + uid + ", username=" + username + ", age=" + age + "]";
}
}
- spring配置
<!-- 构造方法注入
* <constructor-arg> 用于配置构造方法一个参数argument
name :参数的名称
value:设置普通数据
ref:引用数据,一般是另一个bean id值
index :参数的索引号,从0开始 。如果只有索引,匹配到了多个构造方法时,默认使用第一个。
type :确定参数类型
例如:使用名称name
<constructor-arg name="username" value="jack"></constructor-arg>
<constructor-arg name="age" value="18"></constructor-arg>
例如2:类型type 和 索引 index
<constructor-arg index="0" type="java.lang.String" value="1"></constructor-arg>
<constructor-arg index="1" type="java.lang.Integer" value="2"></constructor-arg>
-->
<bean id="userId" class="constructor.User" >
<constructor-arg index="0" type="java.lang.String" value="1"></constructor-arg>
<constructor-arg index="1" type="java.lang.Integer" value="2"></constructor-arg>
</bean>
- 测试代码
package constructor;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestCons {
@Test
public void demo1() throws Exception{
//spring 工厂
String xmlPath = "constructor/beans.xml";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
User user = (User) applicationContext.getBean("userId");
System.out.println(user);
}
}
setter方法
- Person类
package setter;
public class Person {
private String pname;
private Integer age;
private Address homeAddr; //家庭地址
private Address companyAddr; //公司地址
public String getPname() {
return pname;
}
public void setPname(String pname) {
this.pname = pname;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Address getHomeAddr() {
return homeAddr;
}
public void setHomeAddr(Address homeAddr) {
this.homeAddr = homeAddr;
}
public Address getCompanyAddr() {
return companyAddr;
}
public void setCompanyAddr(Address companyAddr) {
this.companyAddr = companyAddr;
}
@Override
public String toString() {
return "Person [pname=" + pname + ", age=" + age + ", homeAddr=" + homeAddr + ", companyAddr=" + companyAddr + "]";
}
}
- Address类
package setter;
public class Address {
private String addr; //地址信息
private String tel; //电话
public String getAddr() {
return addr;
}
public void setAddr(String addr) {
this.addr = addr;
}
public String getTel() {
return tel;
}
public void setTel(String tel) {
this.tel = tel;
}
@Override
public String toString() {
return "Address [addr=" + addr + ", tel=" + tel + "]";
}
}
- spring的xml配置
<!-- setter方法注入
* 普通数据
<property name="" value="值">
等效
<property name="">
<value>值
* 引用数据
<property name="" ref="另一个bean">
等效
<property name="">
<ref bean="另一个bean"/>
-->
<bean id="personId" class="setter.Person">
<property name="pname" value="阳志"></property>
<property name="age">
<value>1234</value>
</property>
<property name="homeAddr" ref="homeAddrId"></property>
<property name="companyAddr">
<ref bean="companyAddrId"/>
</property>
</bean>
<bean id="homeAddrId" class="setter.Address">
<property name="addr" value="阜南"></property>
<property name="tel" value="911"></property>
</bean>
<bean id="companyAddrId" class="setter.Address">
<property name="addr" value="北京八宝山"></property>
<property name="tel" value="120"></property>
</bean>
- 测试代码
package setter;
import org.junit.Test;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
public class TestSetter {
@Test
public void demo2(){
//从spring容器获得
String xmlPath = "setter/beans.xml";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
Person person = (Person) applicationContext.getBean("personId");
System.out.println(person);
}
}
集合注入
- 集合类
package coll;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
public class CollData {
private String[] arrayData;
private List<String> listData;
private Set<String> setData;
private Map<String, String> mapData;
private Properties propsData;
public String[] getArrayData() {
return arrayData;
}
public void setArrayData(String[] arrayData) {
this.arrayData = arrayData;
}
public List<String> getListData() {
return listData;
}
public void setListData(List<String> listData) {
this.listData = listData;
}
public Set<String> getSetData() {
return setData;
}
public void setSetData(Set<String> setData) {
this.setData = setData;
}
public Map<String, String> getMapData() {
return mapData;
}
public void setMapData(Map<String, String> mapData) {
this.mapData = mapData;
}
public Properties getPropsData() {
return propsData;
}
public void setPropsData(Properties propsData) {
this.propsData = propsData;
}
@Override
public String toString() {
return "CollData [\narrayData=" + Arrays.toString(arrayData) + ", \nlistData=" + listData + ", \nsetData=" + setData + ", \nmapData=" + mapData + ", \npropsData=" + propsData + "\n]";
}
}
- spring的xml配置
<!--
集合的注入都是给<property>添加子标签
数组:<array>
List:<list>
Set:<set>
Map:<map> ,map存放k/v 键值对,使用<entry>描述
Properties:<props> <prop key=""></prop> 【】
普通数据:<value>
引用数据:<ref>
-->
<bean id="collDataId" class="coll.CollData" >
<property name="arrayData">
<array>
<value>DS</value>
<value>DZD</value>
<value>屌丝</value>
<value>屌中屌</value>
</array>
</property>
<property name="listData">
<list>
<value>于嵩楠</value>
<value>曾卫</value>
<value>杨煜</value>
<value>曾小贤</value>
</list>
</property>
<property name="setData">
<set>
<value>停封</value>
<value>薄纸</value>
<value>关系</value>
</set>
</property>
<property name="mapData">
<map>
<entry key="jack" value="杰克"></entry>
<entry>
<key><value>rose</value></key>
<value>肉丝</value>
</entry>
</map>
</property>
<property name="propsData">
<props>
<prop key="高富帅">嫐</prop>
<prop key="白富美">嬲</prop>
<prop key="男屌丝">挊</prop>
</props>
</property>
</bean>
- 测试代码
package coll;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestColl {
@Test
public void demo3() throws Exception{
String xmlPath = "coll/beans.xml";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
CollData collData = (CollData) applicationContext.getBean("collDataId");
System.out.println(collData);
}
}
注解
- 注解是用来取代xml配置文件。
1. @Component取代<bean class="">
@Component("id") 取代 <bean id="" class="">
2.web开发,提供3个@Component注解衍生注解(功能一样)取代<bean class="">
@Repository :dao层
@Service:service层
@Controller:web层
3.依赖注入,给私有字段设置,也可以给setter方法设置
普通值:@Value("")
引用值:
方式1:按照【类型】注入
@Autowired
方式2:按照【名称】注入1
@Autowired
@Qualifier("名称")
方式3:按照【名称】注入2
@Resource("名称")
4.生命周期
初始化:@PostConstruct
销毁:@PreDestroy
5.作用域
@Scope("prototype") 多例
为了能够扫描到注解,还需要在xml中配置:
<context:component-scan base-package=.../>
AOP实现原理
- aop底层将采用代理机制进行实现。
- 接口 + 实现类 :spring采用 jdk 的动态代理Proxy。
- 实现类:spring 采用 cglib字节码增强。
jdk动态代理
- 目标接口类
package proxy.a_jdk;
public interface UserService {
public void addUser();
public void updateUser();
public void deleteUser();
}
- 目标实现类
package proxy.a_jdk;
public class UserServiceImpl implements UserService {
@Override
public void addUser() {
System.out.println("proxy.a_jdk addUser");
}
@Override
public void updateUser() {
System.out.println("proxy.a_jdk updateUser");
}
@Override
public void deleteUser() {
System.out.println("proxy.a_jdk deleteUser");
}
}
- 增强代码类
package proxy.a_jdk;
public class MyAspect {
public void before(){
System.out.println("鸡首");
}
public void after(){
System.out.println("牛后");
}
}
- 代理工厂类
package proxy.a_jdk;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class MyBeanFactory {
public static UserService createService(){
//1 目标类
final UserService userService = new UserServiceImpl();
//2切面类
final MyAspect myAspect = new MyAspect();
/* 3 代理类:将目标类(切入点)和 切面类(通知) 结合 --> 切面
* Proxy.newProxyInstance
* 参数1:loader ,类加载器,动态代理类 运行时创建,任何类都需要类加载器将其加载到内存。
* 一般情况:当前类.class.getClassLoader();
* 目标类实例.getClass().get...
* 参数2:Class[] interfaces 代理类需要实现的所有接口
* 方式1:目标类实例.getClass().getInterfaces() ;注意:只能获得自己接口,不能获得父元素接口
* 方式2:new Class[]{UserService.class}
* 例如:jdbc 驱动 --> DriverManager 获得接口 Connection
* 参数3:InvocationHandler 处理类,接口,必须进行实现类,一般采用匿名内部
* 提供 invoke 方法,代理类的每一个方法执行时,都将调用一次invoke
* 参数31:Object proxy :代理对象
* 参数32:Method method : 代理对象当前执行的方法的描述对象(反射)
* 执行方法名:method.getName()
* 执行方法:method.invoke(对象,实际参数)
* 参数33:Object[] args :方法实际参数
*
*/
UserService proxService = (UserService)Proxy.newProxyInstance(
MyBeanFactory.class.getClassLoader(),
userService.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//前执行
myAspect.before();
//执行目标类的方法
Object obj = method.invoke(userService, args);
//后执行
myAspect.after();
return obj;
}
});
return proxService;
}
}
- 测试类
package proxy.a_jdk;
import org.junit.Test;
public class TestJDK {
@Test//执行结果将会是在每一个目标类中的方法调用前后加上增强类中的方法
public void demo01(){
UserService userService = MyBeanFactory.createService();
userService.addUser();
userService.updateUser();
userService.deleteUser();
}
}
cglib动态代理
- 目标类(没有接口)
package proxy.b_cglib;
public class UserServiceImpl {
public void addUser() {
System.out.println("proxy.b_cglib addUser");
}
public void updateUser() {
System.out.println("proxy.b_cglib updateUser");
}
public void deleteUser() {
System.out.println("proxy.b_cglib deleteUser");
}
}
- 增强代码类
package proxy.b_cglib;
public class MyAspect {
public void before(){
System.out.println("鸡首2");
}
public void after(){
System.out.println("牛后2");
}
}
- 代理工厂类
package proxy.b_cglib;
import java.lang.reflect.Method;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
public class MyBeanFactory {
public static UserServiceImpl createService(){
//1 目标类
final UserServiceImpl userService = new UserServiceImpl();
//2切面类
final MyAspect myAspect = new MyAspect();
// 3.代理类 ,采用cglib,底层创建目标类的子类
//3.1 核心类
Enhancer enhancer = new Enhancer();
//3.2 确定父类
enhancer.setSuperclass(userService.getClass());
/* 3.3 设置回调函数 , MethodInterceptor接口 等效 jdk InvocationHandler接口
* intercept() 等效 jdk invoke()
* 参数1、参数2、参数3:与invoke一样
* 参数4:methodProxy 方法的代理
*
*
*/
enhancer.setCallback(new MethodInterceptor(){
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
//前
myAspect.before();
//执行目标类的方法
Object obj = method.invoke(userService, args);
// 执行代理类的父类 ,执行目标类 (目标类和代理类 父子关系)
methodProxy.invokeSuper(proxy, args);//与上面一句代码的执行效果一致
//后
myAspect.after();
return obj;
}
});
//3.4 创建代理
UserServiceImpl proxService = (UserServiceImpl) enhancer.create();
return proxService;
}
}
- 测试类
package proxy.b_cglib;
import org.junit.Test;
public class TestCglib {
@Test
public void demo02(){
UserServiceImpl userService = MyBeanFactory.createService();
userService.addUser();
userService.updateUser();
userService.deleteUser();
}
}
spring aop编程
- 目标接口类
package spring_aop;
public interface UserService {
public void addUser();
public void updateUser();
public void deleteUser();
}
- 目标实现类
package spring_aop;
public class UserServiceImpl implements UserService {
@Override
public void addUser() {
System.out.println("c_spring_aop addUser");
}
@Override
public void updateUser() {
System.out.println("c_spring_aop updateUser");
}
@Override
public void deleteUser() {
System.out.println("c_spring_aop deleteUser");
}
}
- 增强代码类
package spring_aop;
// aop联盟定义的通知类型,必须实现接口
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
/**
* 切面类中确定通知,需要实现不同接口,接口就是规范,从而就确定方法名称。
* * 采用“环绕通知” MethodInterceptor
*
*/
public class MyAspect implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
System.out.println("前");
//手动执行目标方法
Object obj = mi.proceed();
System.out.println("后");
return obj;
}
}
- 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: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">
<!-- 1 创建目标类 -->
<bean id="userServiceId" class="spring_aop.UserServiceImpl"></bean>
<!-- 2 创建切面类(通知) -->
<bean id="myAspectId" class="spring_aop.MyAspect"></bean>
<!-- 3 aop编程
3.1 导入命名空间
3.2 使用 <aop:config>进行配置
proxy-target-class="true" 声明时使用cglib代理
<aop:pointcut> 切入点 ,从目标对象获得具体方法
<aop:advisor> 特殊的切面,只有一个通知 和 一个切入点
advice-ref 通知引用
pointcut-ref 切入点引用
3.3 切入点表达式
execution(* spring_aop.*.*(..))
选择方法 返回值任意 包 类名任意 方法名任意 参数任意
-->
<aop:config proxy-target-class="true">
<aop:pointcut expression="execution(* spring_aop.*.*(..))" id="myPointCut"/>
<aop:advisor advice-ref="myAspectId" pointcut-ref="myPointCut"/>
</aop:config>
</beans>
AspectJ
- 主要用来自定义开发
切入点表达式
execution() 用于描述方法
语法:execution(修饰符 返回值 包.类.方法名(参数) throws异常)
修饰符,一般省略
public 公共方法
* 任意
返回值,不能省略
void 返回没有值
String 返回值字符串
* 任意
包,[省略]
crm 固定包
crm.*.service crm包下面子包任意 (例如:crm.staff.service)
crm.. crm包下面的所有子包(含自己)
crm.*.service.. crm包下面任意子包,固定目录service,service目录任意包
类,[省略]
UserServiceImpl 指定类
*Impl 以Impl结尾
User* 以User开头
* 任意
方法名,不能省略
addUser 固定方法
add* 以add开头
*Do 以Do结尾
* 任意
(参数)
() 无参
(int) 一个整型
(int ,int) 两个
(..) 参数任意
throws ,可省略,一般不写。
基于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: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">
<!-- 1 创建目标类 -->
<bean id="userServiceId" class="aspect.a_xml.UserServiceImpl"></bean>
<!-- 2 创建切面类(通知) -->
<bean id="myAspectId" class="aspect.a_xml.MyAspect"></bean>
<!-- 3 aop编程
<aop:aspect> 将切面类 声明“切面”,从而获得通知(方法)
ref 切面类引用
<aop:pointcut> 声明一个切入点,所有的通知都可以使用。
expression 切入点表达式
id 名称,用于其它通知引用
-->
<aop:config>
<aop:aspect ref="myAspectId">
<aop:pointcut expression="execution(* aspect.a_xml.UserServiceImpl.*(..))" id="myPointCut"/>
<!-- 3.1 前置通知
<aop:before method="" pointcut="" pointcut-ref=""/>
method : 通知,及方法名
pointcut :切入点表达式,此表达式只能当前通知使用。
pointcut-ref : 切入点引用,可以与其他通知共享切入点。
通知方法格式:public void myBefore(JoinPoint joinPoint){
参数1:org.aspectj.lang.JoinPoint 用于描述连接点(目标方法),获得目标方法名等
例如:
<aop:before method="myBefore" pointcut-ref="myPointCut"/>
-->
<!-- 3.2后置通知 ,目标方法后执行,获得返回值
<aop:after-returning method="" pointcut-ref="" returning=""/>
returning 通知方法第二个参数的名称
通知方法格式:public void myAfterReturning(JoinPoint joinPoint,Object ret){
参数1:连接点描述
参数2:类型Object,参数名 returning="ret" 配置的
例如:
<aop:after-returning method="myAfterReturning" pointcut-ref="myPointCut" returning="ret" />
-->
<!-- 3.3 环绕通知
<aop:around method="" pointcut-ref=""/>
通知方法格式:public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable{
返回值类型:Object
方法名:任意
参数:org.aspectj.lang.ProceedingJoinPoint
抛出异常
执行目标方法:Object obj = joinPoint.proceed();
例如:
<aop:around method="myAround" pointcut-ref="myPointCut"/>
-->
<!-- 3.4 抛出异常
<aop:after-throwing method="" pointcut-ref="" throwing=""/>
throwing :通知方法的第二个参数名称
通知方法格式:public void myAfterThrowing(JoinPoint joinPoint,Throwable e){
参数1:连接点描述对象
参数2:获得异常信息,类型Throwable ,参数名由throwing="e" 配置
例如:
<aop:after-throwing method="myAfterThrowing" pointcut-ref="myPointCut" throwing="e"/>
-->
<!-- 3.5 最终通知 -->
<aop:after method="myAfter" pointcut-ref="myPointCut"/>
</aop:aspect>
</aop:config>
</beans>
- 通知类
package aspect.a_xml;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
/**
* 切面类,含有多个通知
*/
public class MyAspect {
public void myBefore(JoinPoint joinPoint){
System.out.println("前置通知 : " + joinPoint.getSignature().getName());
}
public void myAfterReturning(JoinPoint joinPoint,Object ret){
System.out.println("后置通知 : " + joinPoint.getSignature().getName() + " , -->" + ret);
}
public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable{
System.out.println("前");
//手动执行目标方法
Object obj = joinPoint.proceed();
System.out.println("后");
return obj;
}
public void myAfterThrowing(JoinPoint joinPoint,Throwable e){
System.out.println("抛出异常通知 : " + e.getMessage());
}
public void myAfter(JoinPoint joinPoint){
System.out.println("最终通知");
}
}
- 目标接口类
package aspect.a_xml;
public interface UserService {
public void addUser();
public String updateUser();
public void deleteUser();
}
- 目标实现类
package aspect.a_xml;
public class UserServiceImpl implements UserService {
@Override
public void addUser() {
System.out.println("aspect.a_xml addUser");
}
@Override
public String updateUser() {
System.out.println("aspect.a_xml updateUser");
int i = 1/ 0;//手动添加异常
return "返回结果";
}
@Override
public void deleteUser() {
System.out.println("aspect.a_xml deleteUser");
}
}
- 测试代码
package aspect.a_xml;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestAspectXml {
@Test
public void demo1(){
String xmlPath = "aspect/a_xml/beans.xml";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
//获得目标类,这里使用id值获得的直接就是代理对象
UserService userService = (UserService) applicationContext.getBean("userServiceId");
userService.addUser();
userService.updateUser();
userService.deleteUser();
}
}
基于注解
- 目标接口类
package aspect.b_anno;
public interface UserService {
public void addUser();
public String updateUser();
public void deleteUser();
}
- 目标实现类
package aspect.b_anno;
@Service("userServiceId")//用注解的方式代替bean
public class UserServiceImpl implements UserService {
@Override
public void addUser() {
System.out.println("aspect.b_anno addUser");
}
@Override
public String updateUser() {
System.out.println("aspect.b_anno updateUser");
int i = 1/ 0;//手动添加异常
return "返回结果";
}
@Override
public void deleteUser() {
System.out.println("aspect.b_anno deleteUser");
}
}
- 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"
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
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 1.扫描 注解类 -->
<context:component-scan base-package="aspect.b_anno"></context:component-scan>
<!-- 2.确定 aop注解生效 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
- 切面(通知)类
package aspect.b_anno;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
/**
* 切面类,含有多个通知
*/
@Component//用注解代替bean
@Aspect
public class MyAspect {
//切入点当前有效
// @Before("execution(* aspect.b_anno.UserServiceImpl.*(..))")
public void myBefore(JoinPoint joinPoint){
System.out.println("前置通知 : " + joinPoint.getSignature().getName());
}
//声明公共切入点
@Pointcut("execution(* aspect.b_anno.UserServiceImpl.*(..))")
private void myPointCut(){
}
// @AfterReturning(value="myPointCut()" ,returning="ret")
public void myAfterReturning(JoinPoint joinPoint,Object ret){
System.out.println("后置通知 : " + joinPoint.getSignature().getName() + " , -->" + ret);
}
// @Around(value = "myPointCut()")
public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable{
System.out.println("前");
//手动执行目标方法
Object obj = joinPoint.proceed();
System.out.println("后");
return obj;
}
// @AfterThrowing(value="myPointCut()" ,throwing="e")
public void myAfterThrowing(JoinPoint joinPoint,Throwable e){
System.out.println("抛出异常通知 : " + e.getMessage());
}
@After("myPointCut()")
public void myAfter(JoinPoint joinPoint){
System.out.println("最终通知");
}
}
- 测试代码
package aspect.b_anno;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestAspectAnno {
@Test
public void demo2(){
String xmlPath = "aspect/b_anno/beans.xml";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
//获得目标类
UserService userService = (UserService) applicationContext.getBean("userServiceId");
userService.addUser();
userService.updateUser();
userService.deleteUser();
}
}
JdbcTemplate
dbcp
- 首先在数据库中新建一个User表
- 然后创建一个对应的实体类
- spring xml配置
<!-- 创建数据源(dbcp) -->
<bean id="dataSourceId" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/mydatabase"></property>
<property name="username" value="root"></property>
<property name="password" value="1234"></property>
</bean>
<!-- 创建模板 ,需要注入数据源-->
<bean id="jdbcTemplateId" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSourceId"></property>
</bean>
<!-- 配置dao -->
<bean id="userDaoId" class="dbcp.UserDao">
<property name="jdbcTemplate" ref="jdbcTemplateId"></property>
</bean>
- UserDao
package dbcp;
// spring的JdbcTemplate
import org.springframework.jdbc.core.JdbcTemplate;
import domain.User;
public class UserDao {
//jdbc模板将由spring注入
private JdbcTemplate jdbcTemplate;
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
public void update(User user){
String sql = "update t_user set username=?,password=? where id =?";
Object[] args = {user.getUsername(),user.getPassword(),user.getId()};
jdbcTemplate.update(sql, args);
}
}
- 测试代码
package dbcp;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import domain.User;
public class TestDBCP {
@Test
public void demo01(){
User user = new User();
user.setId(1);
user.setUsername("张三");
user.setPassword("1234");
String xmlPath = "dbcp/beans.xml";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
//获得目标类
UserDao userDao = (UserDao) applicationContext.getBean("userDaoId");
userDao.update(user);
}
}
c3p0
<!-- 创建数据源 c3p0-->
<bean id="dataSourceId" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/mydatabase"></property>
<property name="user" value="root"></property>
<property name="password" value="1234"></property>
</bean>
<!-- 创建模板 ,需要注入数据源-->
<bean id="jdbcTemplateId" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSourceId"></property>
</bean>
<!-- 配置dao -->
<bean id="userDaoId" class="c3p0.UserDao">
<property name="jdbcTemplate" ref="jdbcTemplateId"></property>
</bean>
JdbcDaoSupport
- 当dao类继承了JdbcDaoSupport类之后,就可以省略一些关于JdbcTemplate的代码
- spring xml配置文件变成了:
<!-- 创建数据源 c3p0-->
<bean id="dataSourceId" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/mydatabase"></property>
<property name="user" value="root"></property>
<property name="password" value="1234"></property>
</bean>
<!-- 配置dao
* dao 继承 JdbcDaoSupport,之后只需要注入数据源,底层将自动创建模板
-->
<bean id="userDaoId" class="jdbcdaosupport.UserDao">
<property name="dataSource" ref="dataSourceId"></property>
</bean>
- UserDao变成了:
package jdbcdaosupport;
import java.util.List;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.simple.ParameterizedBeanPropertyRowMapper;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
import domain.User;
public class UserDao extends JdbcDaoSupport{
public void update(User user){
String sql = "update t_user set username=?,password=? where id =?";
Object[] args = {user.getUsername(),user.getPassword(),user.getId()};
this.getJdbcTemplate().update(sql, args);
}
/**
* 查询所有
* @return
*/
public List<User> findAll() {
// JdbcTemplate中的查询所有数据的api
return this.getJdbcTemplate().query("select * from t_user", ParameterizedBeanPropertyRowMapper.newInstance(User.class));
}
public User getById(int id) {
// JdbcTemplate中的查询单个数据的api
return this.getJdbcTemplate().queryForObject("select * from t_user where id = ?", ParameterizedBeanPropertyRowMapper.newInstance(User.class), id);
}
}
properties
- 可以将初始化数据源所需要的参数写在一个properties文件中。这样配置文件又会有所更改
- spring xml配置文件
<!-- 加载配置文件
"classpath:"前缀表示 src下
在配置文件之后通过 ${key} 获得内容
-->
<context:property-placeholder location="classpath:properties/jdbcInfo.properties"/>
<!-- 创建数据源 c3p0-->
<bean id="dataSourceId" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driverClass}"></property>
<property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
<property name="user" value="${jdbc.user}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<!-- 配置dao
* dao 继承 JdbcDaoSupport,之后只需要注入数据源,底层将自动创建模板
-->
<bean id="userDaoId" class="properties.UserDao">
<property name="dataSource" ref="dataSourceId"></property>
</bean>
- properties配置文件内容:
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.jdbcUrl=jdbc:mysql://localhost:3306/mydatabase
jdbc.user=root
jdbc.password=1234
事务管理
- 以转账业务为例展示spring的事务管理
基于xml配置
- dao接口类
package dao;
public interface AccountDao {
/**
* 汇款
*/
public void out(String outer , Integer money);
/**
* 收款
*/
public void in(String inner , Integer money);
}
- dao实现类
package dao.impl;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
import AccountDao;
public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao {
@Override
public void out(String outer, Integer money) {
this.getJdbcTemplate().update("update account set money = money - ? where username = ?", money,outer);
}
@Override
public void in(String inner, Integer money) {
this.getJdbcTemplate().update("update account set money = money + ? where username = ?", money,inner);
}
}
- service接口类
package service;
public interface AccountService {
public void transfer(String outer ,String inner ,Integer money);
}
- service实现类
package service.impl;
import dao.AccountDao;
import service.AccountService;
public class AccountServiceImpl implements AccountService {
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
@Override
public void transfer(String outer, String inner, Integer money) {
accountDao.out(outer, money);
//断电
int i = 1/0;
accountDao.in(inner, money);
}
}
- 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"
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.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
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 1 datasource -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/mydatabase"></property>
<property name="user" value="root"></property>
<property name="password" value="1234"></property>
</bean>
<!-- 2 dao -->
<bean id="accountDao" class="dao.impl.AccountDaoImpl">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 3 service -->
<bean id="accountService" class="service.impl.AccountServiceImpl">
<property name="accountDao" ref="accountDao"></property>
</bean>
<!-- 4 事务管理 -->
<!-- 4.1 事务管理器 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 4.2 事务详情(事务通知) , 在aop筛选基础上,对ABC三个确定使用什么样的事务。例如:AC读写、B只读 等
<tx:attributes> 用于配置事务详情(属性属性)
<tx:method name=""/> 详情具体配置
propagation 传播行为 , REQUIRED:必须;REQUIRES_NEW:必须是新的
isolation 隔离级别
-->
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="transfer" propagation="REQUIRED" isolation="DEFAULT"/>
</tx:attributes>
</tx:advice>
<!-- 4.3 AOP编程,目标类有ABCD(4个连接点),切入点表达式 确定增强的连接器,从而获得切入点:ABC -->
<aop:config>
<aop:advisor advice-ref="txAdvice" pointcut="execution(* service..*.*(..))"/>
</aop:config>
</beans>
基于注解
- 基于注解只需要变换两个地方
- 在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"
xmlns:tx="http://www.springframework.org/schema/tx"
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
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 1 datasource -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/mydatabase"></property>
<property name="user" value="root"></property>
<property name="password" value="1234"></property>
</bean>
<!-- 2 dao -->
<bean id="accountDao" class="dao.impl.AccountDaoImpl">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 3 service -->
<bean id="accountService" class="service.impl.AccountServiceImpl">
<property name="accountDao" ref="accountDao"></property>
</bean>
<!-- 4 事务管理 -->
<!-- 4.1 事务管理器 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 4.2 将管理器交予spring
* transaction-manager 配置事务管理器
* proxy-target-class
true : 底层强制使用cglib 代理
-->
<tx:annotation-driven transaction-manager="txManager"/>
</beans>
- 在service实现类中
package service.impl;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import dao.AccountDao;
import service.AccountService;
// Transactional注解写在类之上,对所有方法添加事务管理;写在方法上,只对该方法添加
@Transactional(propagation=Propagation.REQUIRED , isolation = Isolation.DEFAULT)
public class AccountServiceImpl implements AccountService {
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
@Override
public void transfer(String outer, String inner, Integer money) {
accountDao.out(outer, money);
//断电
int i = 1/0;
accountDao.in(inner, money);
}
}
整合进web项目
- 要将spring整合进入web项目中,需要在web.xml中进行配置
<!-- 确定配置文件位置 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<!-- 配置spring 监听器,加载xml配置文件 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>