Spring
核心思想
【IOC(控制反转)】
【IOC(Inversion of control 控制反转)】
定义
? 将对象的创建由原来的new 转移到配置文件中,交给spring 工厂来处理创建对象
<bean id="aa" class="com.zx.UserDaoImpl"></bean>
【DI(Dependency Injection 依赖注入)】
定义
? Spring 不仅要创建对象,还需要建立类与类之间的关系,因此在控制反转的基础上又提出了依赖注入
1、在service组件中生命需要依赖的对象userDao ,并提供set方法
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
2、在applicationContext.xml 配置文件中,将userDao 依赖注入给service
<bean id="aa" class="com.zx.UserDaoImpl">
</bean>
<bean id="userDao" class="com.zx.UserServiceImpl">
<property name="userDao" ref="aa"/>
</bean>
3、测试
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = (UserService) context.getBean("userService");
userService.save("赵翔");
System.out.println(userService);//com.zx.UserServiceImpl@3e9b1010
[注入方式】
? 1、set 注入(使用成员变量方法注入给属性赋值)
? 2、构造注入(使用构造方法形式进行属性赋值)
? 3、自动注入(通过在配置文件中完成属性的赋值)
【SET注入】
1、需要谁就提供谁的成员变量以及set 方法
2、在applicationContext.xml 中声明属性并使用value 进行赋值
public class UserServiceImpl implements UserService {
private UserDao userDao;
private String name;
public void setName(String name) {
this.name = name;
}
<!--对属性name赋值-->
<property name="name" value="肥肥"/>
public void save(String name) {
userDao.save(name);
System.out.println(this.name);
}
3、注入对象类型:引用类型,统一使用ref 进行属性注入
4、数组(普通类型)
? 提供成员变量数组以及set 方法
for (String sz: pp
) {
System.out.println(sz);
}
? 属性赋值
<!--数组赋值-->
<property name="pp" >
<array value-type="java.lang.String">
<value>小肥</value>
<value>小猪</value>
<value>小胖</value>
<value>小仔</value>
</array>
</property>
5、数组(对象类型)
private UserDao[] userDaos;
public void setUserDaos(UserDao[] userDaos) {
this.userDaos = userDaos;
}
System.out.println("数组对象赋值");
for (UserDao sz2: userDaos
) {
System.out.println(sz2);
}
<!--数组对象赋值-->
<property name="userDaos">
<array>
<ref bean="aa"/>
<ref bean="aa"/>
<ref bean="aa"/>
</array>
</property>
Spring默认注入的是单例模式,所以注入三次的三个对象都是同一个
6、List 集合
private List<String> hobbies;
public void setHobbies(List<String> hobbies) { this.hobbies = hobbies;}
System.out.println("==========遍历集合");
hobbies.forEach(hobby-> System.out.println("hobby = "+hobby));
}
<!--List集合-->
<property name="hobbies">
<list>
<value>看书</value>
<value>睡觉</value>
<value>音乐</value>
</list>
</property>
因此,list 用list 标签,普通类型:value 引用 ref
`map`用`map`标签,普通类型:`entry key =xxx value =xxx ` 引用 `key-ref = xxx value-ref = xxx`
? set 用set 标签,普通类型:value 引用 ref
? properties 用prop 标签,key = xxx value
【构造注入】
使用类中的构造方法给属性赋值的方法
语法:
? 1、需要谁就将谁设为成员变量并提供公开的构造方法
? 2、在配置文件中对应的组件标签内部使用<constructor-args> 标签进行注入
private String address;
public UserServiceImpl(String address) { this.address = address;}
System.out.println(this.address);
? index 是指索引,从0开始,name 就是构造方法中的参数,value 就是赋值
<constructor-arg index="0" name="address" value="北京"/>
【自动注入】
? 1、底层原理也是set 方式注入
? 2、自动注入需要在对应组件标签开启才能使用
? 3、【只能用于引用类型的注入|对象类型|组件类型的注入】
语法:
? 1、需要谁就将谁声明为成员变量,并提供set 方法
? 2、在对应的组件标签中上加入autowire 属性并指定自动注入方式即可
1)autowire = byType 根据类型完成自动注入,根据成员变量的类型去spring工厂里找,找到对应赋值,找不到不赋值
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
这里需要的是userDao,就根据userDao的类型去找,找到了就自动注入进行赋值
<bean id="userDao" class="com.zx.UserDaoImpl"></bean>
<bean id="userService" class="com.zx.UserServiceImpl" autowire="byType">
2)autowire = byName 根据名称完成自动注入,根据成员变量的名称去spring工厂里找,找到对应赋值,找不到不赋值
<bean id="userDao" class="com.zx.UserDaoImpl"></bean>
【如果工厂中存在多个类型一致的组件,使用类型注入会报错】
【Spring中工厂创建对象的模式】
【工厂默认在管理对象都是 单例模式 ,单例模式无论在工厂中获取多少次对象都是同一个对象】
1)默认spring 在管理组件对象是 【单例创建】 【singleton】
<bean id="userService" class="com.zx.UserServiceImpl" autowire="byType" scope="singleton">
2)修改为【多例】
<bean id="userService" class="com.zx.UserServiceImpl" autowire="byType" scope="prototype">
scope :用来指定创建工厂的模式 默认值是【单例 singlteton】
如果采用的是【多例】,那么每一次拿到的对象都是崭新的对象
3)spring 工厂创建对象的原理
? 【反射+构造方法】
反射:
<bean id="userService" class="com.zx.UserServiceImpl" autowire="byType">
//工厂原理
UserService user = (UserService) Class.forName("com.zx.UserServiceImpl").newInstance();
System.out.println(user);//com.zx.UserServiceImpl@9660f4e
4)spring 工厂管理组件生命周期
<bean id="userService" class="com.zx.UserServiceImpl" init-method="init" destroy-method="destory">
public void init(){
System.out.println("组件对象初始化");
}
public void destory(){
System.out.println("组建对象销毁");
}
//开启工厂
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//关闭工厂
((ClassPathXmlApplicationContext)context).close();
? a)组件对象什么时候创建?
? 单例对象:工厂启动,所有的单例对象随之创建,工厂销毁,所有的单例对象随之销毁
? 多例对象:每次在工厂中使用时创建 spring 工厂不负责多例对象的销毁 而是jvm 负责垃圾回收
? b)组件对象什么时候销毁?
【spring好处】
1、解耦合,使用配置文件管理java 对象,在生产环境中更换类的实现时不需要重新部署,修改文件即可
2、减少JVM 内存的使用,spring 默认使用单例的模式创建bean ,减少内存的占用
3、 通过【依赖注入】建立组件之间 | 对象之间依赖关系,方便进行组件代码维护和管理
=========================================================================================
【AOP 面向切面编程】
【(proxy)代理的引言】
? 1、代理对象:在整个业务逻辑中完成(传话)(附加操作)的作用,同时也可以中断整个业务
? 2、【好处】:既可以保证原始业务逻辑,原始业务功能不变的情况同时还可以完成一些附加的操作
【如何开发一个代理对象】
? 1、代理对象和业务逻辑对象 | 原始逻辑对象 | 真正的业务逻辑对象 实现相同的接口【小丽闺蜜必须和小丽实现同一个接口,才能和小张进行聊天】
? 2、 代理对象中是【依赖】原始的业务逻辑对象 【小丽闺蜜在传话的时候离不开小丽本人】
创建代理对象的目的:保证原始功能不变的情况下 完成业务逻辑中附加操作 业务层更专注于业务逻辑的开发
【静态代理】
? 1、为每一个业务逻辑通过手动的方法开发对应的代理对象的过程
? 2、在配置文件中注入目标对象
<bean id="userService" class="com.staticProcy.UserServiceImpl"/>
<bean id="userServiceProxy" class="com.staticProcy.UserServiceProxy">
<!--依赖真正的业务逻辑对象-->
<property name="userService" ref="userService"/>
原始业务逻辑对象:Target Object | 目标对象
【动态代理】
public class TestDynamicProxy {
public static void main(String[] args) {
//使用动态代理对象:指的是在程序运行的过程中动态通过代码的方式为指定的类生成动态代理对象
//Proxy: 用来生成动态代理的类
//参数1 : classLoader 类加载器
//参数2 : Class[] 创建动态代理对象的接口的数组
//参数3 : InvocationHandler: invoke()
//返回值: 就是创建好的动态代理对象
//目标类
UserService userService = new UserServiceImpl();
System.out.println(userService);
//原始实现com.staticProcy.UserServiceImpl@6f94fa3e
//读取类信息,加载到内存中,才能为他对应的生成动态代理对象
//所以需要类加载器来加载类信息
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
Class[] classes = {UserService.class};
UserService proxy = (UserService) Proxy.newProxyInstance(classLoader, classes, new InvocationHandler() {
@Override
//在通过动态代理对象调用自己的代理方法时,会优先执行invocationHandler类中的invoke
/*
* 参数1:当前创建好的代理对象
* 参数2:当前代理对象执行的方法对象
* 参数3: 当前代理对象执行方法的参数
* */
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//在用mybatis生成mapper文件的时候,也是没有手动写实现类的,底层同样是使用了动态代理的理念
//userDao = sqlSession.getMapper(userDao.class)
//mybatis 的 mapper文件的解析就是在invoke方法中执行的 根据method.getName()获得方法名,去找到id="方法名" //再用sql+jdbc+pstm args 进行解析
//因此方法不能重载,要保证方法名的唯一性
System.out.println("开启事务");
System.out.println("当前执行的方法"+method.getName());
System.out.println("当前方法的参数:"+args[0]);
//开启事务
//当前执行的方法findAll
//当前方法的参数:赵翔
try {
System.out.println("开启事务");
//调用目标类中业务方法【通过反射机制 调用目标类中的当前的方法】
Object invoke = method.invoke(new UserServiceImpl(),args);
System.out.println("提交事务");
return invoke;
}catch (Exception e){
e.printStackTrace();
System.out.println("回滚事务");
}
return null;
}
});
System.out.println(proxy.getClass());
//动态代理对象 class com.sun.proxy.$Proxy0
String result = proxy.findAll("赵翔");//通过动态代理对象调用代理中的方法
System.out.println(result);
}
}
面向切面编程底层就是java代理设计模式 【动态代理】
好处:
? 在保证原始业务功能不变的情况下,通过【代理对象】完成业务中的附加操作
? 将业务中核心操作放在目标对象中执行,实现了附加操作和核心操作解耦
通知(Advice):除了目标方法意外的操作都称之为通知,附加功能、事务通知、日志通知、性能通知…
切入点(Pointcut):指定开发好的通知 应用于项目中哪些组件中哪些方法 一般多用于service层
切面(Aspect) = 通知(Advice) + 切入点(Pointcut)
在执行代理方法之前的通知叫
【前置通知】:MethodBeforeAdvice
之后的叫【后置通知】:AfterReturningAdvice
异常的叫【异常通知】:ThrowsAdvice
【环绕通知】:MethodIntercept -----> 【事务】
是所有通知类型中功能最为强大的, 能够全面地控制连接点. 甚至可以控制是否执行连接点.
对于环绕通知来说, 连接点的参数类型必须是 ProceedingJoinPoint . 它是JoinPoint 的子接口, 允许控制何时执行, 是否执行连接点.
在环绕通知中需要明确调用 ProceedingJoinPoint 的proceed() 方法来执行被代理的方法. 如果忘记这样做就会导致通知被执行了, 但目标方法没有被执行.
注意: 环绕通知的方法需要返回目标方法执行之后的结果, 即调用 joinPoint.proceed(); 的返回值, 否则会出现空指针异常
AOP 面向切面编程 :1、开发通知类(附加功能) 2、配置切面点 3、组装切面(Aspect)
步骤:
? 1、加入aop相关依赖 spring-aop、spring-expression、spring-aspect
? 2、项目开发额外功能通知
? 3、配置切面 apllicationContext.xml
? a)注册通知类 <bean id = "" class = "xxxAdvice">
? b)组装切面aspect = advice + pointcut
? <aop:config>
? <aop:pointvut>
? <aop:advisort>
? </aop:config>
前置通知
# 目标对象
public class EmpServiceImpl implements EmpService {
@Override
public void save(String name) {
System.out.println("处理业务逻辑调用save Dao"+name);
}
@Override
public String find(String name) {
System.out.println("处理业务逻辑调用save Dao"+name);
return name;
}
# 需要实现前置通知类MethodBeforeAdvice
//自定义记录业务方法名称前置通知(目标方法执行之前先执行的额外操作)
public class MyBeforeAdvice implements MethodBeforeAdvice {
//before
/*
* 参数1:当前执行方法对象、
* 参数2:当前执行方法的参数
* 参数3:目标对象 * */
@Override
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println("===========开启日志记录=========");
System.out.println("当前执行的方法:"+method.getName());
System.out.println("当前执行的方法参数:"+objects);
System.out.println("当前执行的方法的目标对象:"+o);
System.out.println("===========结束日志记录=========");
}
# applicationContext.xml文件
<!--管理Service组件对象-->
<bean id="empService" class="com.aop.EmpServiceImpl"></bean>
<!--注册通知-->
<bean id="myBeforeAdvice" class="com.aop.MyBeforeAdvice"/>
<!--组装切面-->
<aop:config >
<!--配置切入点 id:切入点在工厂中的唯一标识
expression表达式:用来指定切入项目中哪些组件哪些方法
execution(返回值 包.类名.*(..)) -->
<!--<aop:pointcut id="pc" expression="execution(* com.aop.EmpServiceImpl.*(..))"/>-->
<!--配置切面
advice-ref:工厂中通知id
pointcut-ref:工厂切入点唯一标识
-->
<aop:advisor advice-ref="myBeforeAdvice" pointcut-ref="pc"/> </aop:config>
配置文件中管理了service 组件
注册通知 需要创建通知类实现对应的类方法
配置切入点,声明对哪个组件的哪个方法进行切入
将通知和切入点的id进行绑定配置
测试:
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("/aop/applicationContext.xml");
EmpService empService = (EmpService) context.getBean("empService");
empService.save("赵翔");
}
===开启日志记录= 当前执行的方法:save 当前执行的方法参数:[Ljava.lang.Object;@23282c25 当前执行的方法的目标对象:com.aop.EmpServiceImpl@7920ba90 ===结束日志记录= 处理业务逻辑调用save Dao赵翔
只要是被切入的方法就不再是普通的对象而是代理对象,代理对象执行
【环绕通知】
public class DeptServiceImpl implements DeptService {
private DeptDAO deptDAO;
public void setDeptDAO(DeptDAO deptDAO) {
this.deptDAO = deptDAO;
}
@Override
public String find(String name) {
System.out.println("处理find业务逻辑");
String s = deptDAO.find(name);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
throw new RuntimeException("执行出错了!!!!!!");
}
自定义环绕通知
//自定义环绕通知,记录目标的方法的执行时长
public class MethodTimeAdvice implements MethodInterceptor {
/*
这里就是动态代理的invoke方法
参数是被spring包装成了methodInvocation
methodInvocation:获取当前执行方法 获取当前执行方法的参数 获取目标对象
* */
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
System.out.println("进入环绕通知");
/* System.out.println("当前执行方法:"+methodInvocation.getMethod().getName());
System.out.println("当前方法的参数:"+methodInvocation.getArguments()[0]);
System.out.println("当前目标对象:"+methodInvocation.getThis());*/
try {
long start = new Date().getTime();
//放行目标方法
Object proceed = methodInvocation.proceed();//!!!!!!!!!!!重点!
long end = new Date().getTime();
System.out.println(methodInvocation.getMethod().getName()+"方法执行所用时间为:"+(end-start)+"秒");
return proceed;//回到目标方法执行
}catch (Exception e){
e.printStackTrace();
System.out.println("出现异常!");
}
return null;
}
配置
<!--管理DAO组件-->
<bean id="deptDAO" class="com.round.impl.DeptDAOImpl"></bean>
<!--管理service组件-->
<bean id="deptService" class="com.round.service.impl.DeptServiceImpl">
<!--注入-->
<property name="deptDAO" ref="deptDAO"/>
</bean>
<!--注册通知类-->
<bean id="methodTimeAdvice" class="com.round.advices.MethodTimeAdvice"></bean>
<!--配置切面-->
<aop:config>
<aop:pointcut id="pc" expression="execution(* com.round.service.impl.*ServiceImpl.*(..))"/>
<!--组装切面-->
<aop:advisor advice-ref="methodTimeAdvice" pointcut-ref="pc"/>
</aop:config>
测试
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("/round/applicationContext.xml");
DeptService deptService = (DeptService) context.getBean("deptService");
System.out.println(deptService.getClass().getName());
deptService.find("赵翔");
}
切入点表达式
1、expression="execution()"
? 切入点表达式------->方法级别的切入点表达式 控制粒度 :方法级别 效率低
? 完整语法:
? 1、execution(访问权限修饰符 返回值 包名.类名.方法名(参数类型))
? 2、execution(返回值 包名.类名.方法名(参数类型))
? *任意多个字符
例如:1)execution(* com.zx.service.*.*(..)) 表示com.zx.service包下的任意方法任意参数任意返回值
? 2)execution(* com.zx.service..*.*(..)) 表示com.zx.service包及子包中的子包下的任意方法任意参数任意返回值 ? 3)execution(* *.*(..)) 表示全部方法 避免使用 2、expression="within()" 切入点表达式------->类级别的切入点表达式 控制粒度 :类级别 效率高
? 完整语法:
? 1、within (包名.类名)
例如:1)within(com.zx.service.*ServiceImpl) 表示service包下的所有以ServiceImpl 结尾的类
有关异常通知,不是必须重写的,但是需要有方法存在。
异常通知不可以和后置通知同时存在,因为后置通知肯定是在目标方法成功执行之后执行的
Spring如何创建复杂对象
Spring原理是什么,是如何创建对象的?
1)组件对象 UserDAO userDAOImpl UserService UserServiceImpl
2)工厂创建
<bean id = "userDAO" class = ""com.baizhi.dao.UserDAOImpl>
3)获得组件对象
UserDAO userDAO = (UserDAO) new ClassPathXmlApplicationContext("applicationContext.xml");
4)工厂原理【反射机制,Class.forName(“com.baizhi.dao.UserDAOImpl”).newInstance();】调用类中构造方法进行创建对象
通过工厂创建简单对象
? 简单对象:类中含有构造方法的,可以直接通过new 关键字创建的对象 统一称为简单对象
? 工厂创建时:<bean id="" class=""> `
通过工厂创建复杂对象
? 复杂对象:不能直接通过new 关键字创建的对象
? 比如接口类型(jdbc 中的Connection )、抽象类类型(Calendar (日历)、MessageDigest (加密))
? Calendar 需要用getCalendar() 静态方法来创建对象
? Connection 需要用DriverManager.getConnection("url","username","password") 来创建对象
Spring工厂创建复杂对象的方式:
类 implements FactoryBean<Calendar | Connection | 想要创建的复杂对象>{}
所以需要创建复杂对象的时候,需要自己写一个类来实现FactoryBean<> ,<>中就是你想创建的复杂对象
步骤:
1、创建复杂对象类实现FactoryBean<复杂对象>
public class CalendarFactoryBean implements FactoryBean<Calendar> {
//用来书写复杂对象的创建方式
@Override
public Calendar getObject() throws Exception {
return Calendar.getInstance();
}
//指定创建的复杂对象的类型
@Override
public Class<?> getObjectType() {
return null;
}
//用来指定创建的对象的模式,true是单例,false是多例
@Override
public boolean isSingleton() {
return false;
}
}
2、通过工厂配置创建复杂对象
<!--通过factoryBean创建复杂对象--> <bean id="calendarFactoryBean" class="com.zx.factoryBean.CalendarFactoryBean"/>
3、测试
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Calendar factoryBean = (Calendar) context.getBean("calendarFactoryBean");
System.out.println(factoryBean.getTime());
}
Spring整合Mybatis
1、加入依赖 mysql spring mybatis mybatis-spring durid spring-tx
2、配置applciationContext.xml
<!--注入-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306:mimissm"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--依赖数据源对象-->
<property name="dataSource" ref="dataSource"/>
</bean>
进入SqlSessionFactoryBean 源码可以看到,同样是实现了FactoryBean ,就如上面管理复杂对象一样,需要手动创建XxxFactoryBean 来实现FactoryBean 。
在mybatis-spring jar 包中,封装了sqlSessionFactory 对象的创建
public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent>
private Resource configLocation;
private Configuration configuration;
private Resource[] mapperLocations;
private DataSource dataSource;
注意:使用了官方提供的sqlSessionFactoryBean 后,就不能再使用mybatis-config.xml
Spring整合Mybatix的DAO开发
1、domain
2、dao接口
3、mapper
<mapper namespace="com.zx.dao.UserDao">
<select id="findAll" resultType="com.zx.domain.User">
select id,name,age from user;
</select>
</mapper>
4、applicationContext.xml
<!--创建sqlSessionFactoryBean对象-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--依赖数据源-->
<property name="dataSource" ref="dataSource"/>
<!--注册mapper配置文件-->
<property name="mapperLocations" value="classpath:com/zx/mapper/UserDaoMapper.xml"/>
<!-- 配置事务性-->
<property name="transactionFactory">
<bean class="org.apache.ibatis.transaction.managed.ManagedTransactionFactory" />
</property>
</bean>
<!--创建数据源-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/springboot"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
5、测试
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
SqlSessionFactory sqlSessionFactory = (SqlSessionFactory) context.getBean("sqlSessionFactory");
SqlSession sqlSession = sqlSessionFactory.openSession();
UserDao userDao = sqlSession.getMapper(UserDao.class);
userDao.findAll().forEach(user -> System.out.println(user));
}
继续封装:
测试
在这里直接找到bean为userDao的id,userDao下注入了sqlSessionFactory 和创建的接口,然后 sqlSessionFactory 依赖数据源,最后访问到数据,所以这里测试就不需要再写 SqlSession sqlSession = sqlSessionFactory.openSession(); 和UserDao userDao = sqlSession.getMapper(UserDao.class);
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserDao userDao = (UserDao) context.getBean("userDao");
userDao.findAll().forEach(user -> System.out.println(user));
}
配置文件,MapperFactoryBean 同样实现了FactoryBean
<!--创建DAO组件类-->
<bean id="userDao" class="org.mybatis.spring.mapper.MapperFactoryBean">
<!--注入sqlSessionFactory-->
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
<!--注入所创建的dao接口-->
<property name="mapperInterface" value="com.zx.dao.UserDao"/>
</bean>
log4j
1、 引入依赖
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.14.1</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.30</version>
</dependency>
2、引入log4j.properties 配置文件
? 【注意:必须放在resources根目录下,名字叫log4j.properties】
# 根日志
log4j.rootLogger=ERROR,bb
log4j.appender.bb=org.apache.log4j.ConsoleAppender
log4j.appender.bb.layout=org.apache.log4j.PatternLayout
log4j.appender.bb.layout.conversionPattern=[%p] %d{yyyy-MM-dd} %m%n
#子日志
log4j.logger.com.zx.dao=DEBUG
log4j.logger.org.springframework=DEBUG
事务
propagation:事务传播属性
? REQUIRED :需要事务,如果外层没有事务,则开启新的事务,如果有,融入当前事务(增删改)
? SUPPORTS :支持事务如果外层没有事务,不会开启新的事务,如果有,融入当前事务(查)
? REQUIRES_NEW :每次开启新事务 如果外层存在事务,外层事务挂起,自己开启新的事务,执行完成,恢复外层事务继续执行
? NOT_SUPPORT :不支持事务,如果外层存在事务,外层事务挂起,自己以非事务方式执行,执行完成,恢复外层事务执行
? NEVER :不能存在事务 存在事务报错
? MANDATORY :强制事务 没有事务报错
? NESTED :嵌套事务 事物之间可以嵌套运行
?
<!--设置事务管理器--><bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--添加事务的切面-->
<tx:advice id="myadvice" transaction-manager="transactionManager">
<tx:attributes>
<!--查询的时候,只读,不会因为增删改而产生影响-->
<tx:method name="*select*" read-only="true"/>
<tx:method name="*find*" read-only="true"/>
<tx:method name="*get*" read-only="true"/>
<tx:method name="*search*" read-only="true"/>
<!--增删改 需要支持事务-->
<tx:method name="*insert*" propagation="REQUIRED"/>
<tx:method name="*add*" propagation="REQUIRED"/>
<tx:method name="*save*" propagation="REQUIRED"/>
<tx:method name="*update*" propagation="REQUIRED"/>
<tx:method name="*modify*" propagation="REQUIRED"/>
<tx:method name="*set*" propagation="REQUIRED"/>
<tx:method name="*change*" propagation="REQUIRED"/>
<tx:method name="*delete*" propagation="REQUIRED"/>
<tx:method name="*clear*" propagation="REQUIRED"/>
<tx:method name="*remove*" propagation="REQUIRED"/>
<!--如果都不匹配 那么就支持事务即可 不需要添加事务-->
<tx:method name="*" propagation="SUPPORTS"/>
</tx:attributes>
</tx:advice>
isolation:事务隔离级别
DEFAULT :使用数据库默认的隔离级别(推荐)
READ_UNCONMMITTED :读未提交 一个客户端读到了另一个客户端没有提交的数据(脏读)
READ_CONMMITTED :读提交 用来避免脏读现象 一个客户端只能读到另一个客户端提交的数据【Oracle】
REPEATABLE_READ :可重复读 主要是用来避免不可重复读现象 也叫 【行锁】 【MySQL】
? 【不可重复读:在一次业务操作中,多次读同一条数据读出数据不一致的现象】
SERIALIZABLE :序列化 最高隔离级别 主要是用来避免 幻读 现象 【表加锁】
? 【幻读:在一次业务操作中,多次读数据发现数据条数不一致,幻影读】
【注意:隔离级别越高,对项目中的查询效率越低 一般推荐数据库默认隔离级别】
read-only :事务读写性,true:只读,不能执行增删改的操作 false 可读可写【MySQL支持,Oracle不支持】
rollback-for :出现什么异常回滚---->默认出现RunTimeExeception 及其子类异常进行回滚
no-rollback-for :出现什么异常不回滚 例如 no-rollback-for = "java.lang.RunTimeExeception"
timeout :事务超时性 -1 永不超时 >0 代表设置超时时间 单位 秒
注解式开发
前置条件:在Spring工厂中配置注解扫描
? <context:component-scan base-package="com.zx"/>
1、创建对象相关注解
1、@Component注解
? a)用来负责对象的创建 只能用在类上 <bean id="" class = ""/>
? b)默认使用这个注解在工厂中的对象名是 类名首字母小写
? c)有一个value 属性,作用就是指定对象在工厂中的唯一标识,也就是id
2、@Repository注解
? a)一般用来创建DAO 中组件的注解
3、@Controller
? a)一般用来创建Controller 组件中的注解
4、@Service
? a)一般用来创建Service 组件中的注解
2、控制对象在工厂中创建次数
a、配置文件中修改<bean id = "" class = "" scope = "singleton | prototype"/>
b、注解式: @Scope 用来指定对象的创建次数 【默认单例】 只能加载类上 value 属性:singleton 单例 prototype 多例
3、属性注入的相关注解
? a.·spring·框架提供的 @Autowire 默认根据类型注入
? b.·javaEE·中本身就有 @Resource 默认根据名字注入 名称找不到再根据类型注入
修饰范围:用在类中的成员变量上,或者是类中成员变量的SET 方法上
作用:用来完成成员变量的赋值 | 注入操作
【注意:使用注解进行注入时, 日后注入的成员变量可以不再提供SET 方法 】
4 控制事务注解
一般用在业务层
@Transactional
? 作用:用来给类中方法加入事务控制,简化配置文件中的 事务通知及事务细粒度配置 以及 简化事务切面配置
修饰范围:类 | 方法上 局部优先原则
? 1、加在类上 代表类中所有方法加入事务控制
? 2、加在方法上 代表当前方法加入事务控制
? 3、类和方法上同时存在 方法优先
使用需求:想要·@Transactional·生效,必须在配置文件中加入
1、设置事务管理器
<!--设置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
2、开启注解式事务生效
<tx:annotation-driven transaction-manager="transactionManager"/>
tory注解
? a)一般用来创建DAO 中组件的注解
3、@Controller
? a)一般用来创建Controller 组件中的注解
4、@Service
? a)一般用来创建Service 组件中的注解
2、控制对象在工厂中创建次数
a、配置文件中修改<bean id = "" class = "" scope = "singleton | prototype"/>
b、注解式: @Scope 用来指定对象的创建次数 【默认单例】 只能加载类上 value 属性:singleton 单例 prototype 多例
3、属性注入的相关注解
? a.·spring·框架提供的 @Autowire 默认根据类型注入
? b.·javaEE·中本身就有 @Resource 默认根据名字注入 名称找不到再根据类型注入
修饰范围:用在类中的成员变量上,或者是类中成员变量的SET 方法上
作用:用来完成成员变量的赋值 | 注入操作
【注意:使用注解进行注入时, 日后注入的成员变量可以不再提供SET 方法 】
4 控制事务注解
一般用在业务层
@Transactional
? 作用:用来给类中方法加入事务控制,简化配置文件中的 事务通知及事务细粒度配置 以及 简化事务切面配置
修饰范围:类 | 方法上 局部优先原则
? 1、加在类上 代表类中所有方法加入事务控制
? 2、加在方法上 代表当前方法加入事务控制
? 3、类和方法上同时存在 方法优先
使用需求:想要·@Transactional·生效,必须在配置文件中加入
1、设置事务管理器
<!--设置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
2、开启注解式事务生效
<tx:annotation-driven transaction-manager="transactionManager"/>
|