在servlet中需要调用service中的方法,则需要在servlet类中通过new关键字创建service 的实例
service
public interface productService{ public list<product> list products(); }
public class productServiceImpl1 implements ProductService{ public list<Product> listOriducts(){ //查询热销商品 } }
public class productServiceImpl2 implements ProductService{ public list<Product> listOriducts(){ //查询好评商品 } }
public class ProductListServlet extends HttpServlet{ //在servlet中使用new 关键字 创建 productServiceImpl 对象 //增加了servlet和service的耦合度 }
在service实现类中需要调用DAO中的方法,也需要在service实现类通过new关键字创建DAO实现类对象
如果在使用new关键字创建对象 则会导致以下问题:
耦合度
解决方案: 在servlet中定义service接口的对象变了,不使用new关键字创建实现类对象,在servlet的实例化的时候,通过反射动态的给service对象变量赋值。
解决方案
如何实现? spring可以做到
如何实现?
spring是一个轻量级的控制反转和面向切面的容器框架,用来解决企业项目开发的复杂度问题-->解藕
轻量级的控制反转和面向切面的容器
spring-->润物细无声没有具体实质性的功能 Mybatis数据库操作 Spring MVC数据接收(控制者) Spring 1.帮助创建管理对象 2.增强项目的业务 --> 帮助别人的框架使用更方便
润物细无声
Spring容器组件,用于完成实例的创建和管理 Core 核心 beans 依赖于核心 做对象管理或 实例管理 Context Spring上下文或容器上下文
Spring容器组件,用于完成实例的创建和管理
上面两个组成Spring AOP组件 实现面向切面编程 AOP Aspects
上面两个组成Spring AOP组件 实现面向切面编程
Spring web组件 实际上指的是 Spring MVC框架,实现web项目的MVC控制 web组件(Spring对web项目的支持) web MCVC (Spring MVC组件)
Spring web组件 实际上指的是 Spring MVC框架,实现web项目的MVC控制
Spring 数据访问组件 也是基于JDBC封装的持久层框架(即使没有Mybatis Spring也可以完成持久化) tx 事物管理组件
Spring 数据访问组件 也是基于JDBC封装的持久层框架(即使没有Mybatis Spring也可以完成持久化)
Spring的单元测试组件 提供了Spring环境下的单元测试支持 test
Spring的单元测试组件 提供了Spring环境下的单元测试支持
Spring ioc 容器组件,可以完成对象的创建 对象属性赋值 对象管理
counext
<!--Spring beans--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>5.2.13.RELEASE</version> </dependency> <!--Spring core--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>5.2.13.RELEASE</version> </dependency> <!--Spring context--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.13.RELEASE</version> </dependency>
通过配置文件“告诉” Spring 容器创建什么对象 给对象属性赋什么值
applictionCountext.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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!--对于一个xml文件如果作为框架的配置文件,需要遵守框架的配置规则--> <!--通常一个框架为了让开发者能够正确配置,都会提供xml的规范文件(dtd/xsd)--> </beans>
使用Springioc 组件创建并管理对象
/** * @author WanShen * @date 2022年02月24日 11:30 PM */ public class Student { private String stuNum; private int stuAge; private String stuSex; private Date enterenceTime; //入学日期 }
<?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"> <!--通过bean将实体类配置给Spring进行管理 ID表示 实体类的唯一标识--> <bean id="student" class="com.bean.Student"> <property name="stuNum" value="张三" /> <property name="stuAge" value="13" /> <property name="stuSex" value="男" /> </bean> </beans>
//通过Spring容器创建student对象 //1.初始化Spring容器 ClassPathXmlApplicationContext context= new ClassPathXmlApplicationContext("applicationContext.xml"); //2.通过Spring容器获取student对象 Student student = (Student) context.getBean("student"); System.out.println(student);
当我们需要通过Spring对象工厂创建某个累的对象的时候,需要将这个类交给Spring管理-->通过bean标签配置
<bean id="student" class="com.bean.Student"></bean> <bean id="bool" class="com.bean.Book"></bean>
通过Spring容器给创建的对象属性赋值
<bean id="student" class="com.bean.Student"> <property name="stuNum" value="张三" /> <property name="stuAge" value="13" /> <property name="stuSex" value="男" /> <property name="enterenceTime" ref="date" /> </bean>
Spring容器加载配置文件之后通过反射创建类的对象,并给属性赋值;Spring通过反射实现属性注入有三种方式: set方法注入 构造器注入 接口注入(不常用)
Spring容器加载配置文件之后通过反射创建类的对象,并给属性赋值;Spring通过反射实现属性注入有三种方式:
在bean标签中通过配置property标签给对象属性赋值,实际上就是通过反射调用set方法完成属性的注入
简单类型及字符串简单类型指String double这些
简单类型及字符串
日期对象
<bean id="student" class="com.bean.Student"> <property name="enterenceTime" ref="date" /> </bean> <bean id="date" class="java.util.Date"/>
<bean id="student" class="com.bean.Student"> <property name="enterenceTime"> <bean class="java.util.Date" /> </property> </bean>
集合类型
List
<property name="hobbit" value="看电影,旅游" />
<property name="hobbit"> <list> <value>看电影</value> <value>旅游</value> </list> </property>
<property name="hobbit"> <list> <bean class="com.bean.ClassZz" /> <bean class="com.bean.ClassZz" /> <bean class="com.bean.ClassZz" /> </list> </property>
<property name="hobbit"> <list> <ref bean="cls"></ref><!--引用容器中的bean--> <ref bean="cls"></ref> <ref bean="cls"></ref> </list> </property>
set
<property name="classZz"> <set> <!--和List元素注入方式相同--> </set> </property>
Map
<property name="map"> <map> <entry> <key> <value>k1</value> </key> <value>123</value> </entry> <entry> <key> <value>k2</value> </key> <value>456</value> </entry> </map> </property>
Properties 可以直接给键值对 (String类型)
<property name="properties"> <props> <prop key="k1">123</prop> <prop key="k2">456</prop> </props> </property>
使用被Spring管理的类的构造器来完成属性的赋值 常用set方法注入 构造器注入使用少
简单类型 字符串 对象 需要对应顺序 可以使用index索引
<bean id="cls" class="com.bean.ClassZz"></bean> <bean id="stu" class="com.bean.Student"> <constructor-arg value="张三" /> <!--<constructor-arg index="0" value="张三" />--> <constructor-arg value="18" /> <constructor-arg value="男" /> <constructor-arg value="162.5" /> <constructor-arg> <bean class="java.util.Date"></bean> </constructor-arg> <constructor-arg ref="cls" /> </bean>
集合类型属性:
public class Student{ private List<String> hobbit; //爱好 private Properties properties; private Map map; private Set set; public Student(Properties properties, Map map, Set set) { this.properties = properties; this.map = map; this.set = set; } }
<bean id="stu1" class="com.bean.Student"> <constructor-arg> <list> <value>111list</value> </list> </constructor-arg> <constructor-arg> <map> <entry> <key><value>key1</value></key> <value>values1</value> </entry> </map> </constructor-arg> <constructor-arg> <set> <value>set集合</value> </set> </constructor-arg> <constructor-arg> <props> <prop key="key22">propers</prop> </props> </constructor-arg> </bean>
在bean标签中可以通过scope属性 指定对象的作用域 scope=“singleton” 表示当前bean是单例模式(默认饿汉模式,Spring容器初始化阶段就会完成此对象的创建;当在bean标签中设置lazy-init=“true”变为懒汉模式) scope=“prorotype”表示当前bean为非单例模式,每次通过Spring容器获取此bean对象的时候都会创建一个新的对象
在bean标签中可以通过scope属性 指定对象的作用域
lazy-init=“true”
单例
<bean id="cls" class="com.bean.ClassZz" scope="singleton" lazy-init="true"></bean>
多例
<bean id="cls" class="com.bean.ClassZz" scope="prototype"></bean>
在Bean标签中通过init-method属性指定当前bean的初始化方法 初始化方法在构造器之后执行 在Bean标签中通过destroy-method=属性指定当前bean的销毁方法 销毁方法在对象销毁之前执行
package com.bean; /** * @author WanShen * @date 2022年02月26日 4:28 PM */ public class Book { private int bookId; private String bookName; public void init(){ System.out.println("------init"); this.bookId=1; this.bookName="初始值"; } public void destroy(){ System.out.println("销毁!"); } public Book() { } @Override public String toString() { return "Book{" + "bookId=" + bookId + ", bookName='" + bookName + '\'' + '}'; } }
<bean id="book" class="com.bean.Book" scope="prototype" init-method="init" destroy-method="destroy"></bean>
自动装配: Spring在实例化当前Bean的时候从Spring容器中找到匹配的实例赋值给当前Bean的属性 byName 根据当前bean的属性名在Spring容器中·寻找匹配的对象 如果根据name找到了bean但是类型不匹配则抛出异常 byType 根据当前bean的属性类型在Spring容器中寻找匹配的对象 如果根据类型找到了多个类型匹配的bean也会抛出异常
自动装配: Spring在实例化当前Bean的时候从Spring容器中找到匹配的实例赋值给当前Bean的属性
<bean id="student" class="com.bean.Student" autowire="byName" ></bean> <bean id="student" class="com.bean.Student" autowire="byType" ></bean>
Spring ioc的使用需要我们通过xml将类声明给Spring容器进行管理从而通过Spring工厂完成对象的创建及属性值的注入; Spring除了提供基于xml的配置方式,同时提供了基于注解的配置,直接在实体类中添加注解声明给Spring容器管理 以简化开发步骤
Spring ioc的使用需要我们通过xml将类声明给Spring容器进行管理从而通过Spring工厂完成对象的创建及属性值的注入;
Spring除了提供基于xml的配置方式,同时提供了基于注解的配置,直接在实体类中添加注解声明给Spring容器管理 以简化开发步骤
略过
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.13.RELEASE</version> </dependency>
在applicationContext.xml声明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"> <!--声明使用注解--> <context:annotation-config/> <!-- 声明Spring工厂注解的扫描范围--> <context:component-scan base-package="com.beans"/> </beans>
@Component
@Component(value="stu")
@Scope
类注解
@Lazy
@PostConstruct
@PreDestroy
@Autowired
属性注解,方法注解(set方法) 声明当前属性自动装配,默认byType
@Autowired(required = false) 通过required属性设置当前自动装配是否为必须 默认必须(如果没有找到类型与属性类型匹配的bean则抛出异常)
@Autowired public void setClazz(@Qualifier("c1") Clazz clazz) { this.clazz = clazz; }
@Resource
代理设计模式的优点:将通用性的工作都交给代理对象完成✅ 被代理对象只需专注自己的核心业务。
静态代理,代理类只能够为特定的生产代理对象,不能代理任意类 流程: BookDAO(接口) BookDAOImpl(接口实现类) Myproxy(代理类) 两个以上的话 代理类封装接口-->提供有参构造方法--> 通过代理可以两个类切换
静态代理,代理类只能够为特定的生产代理对象,不能代理任意类
流程: BookDAO(接口) BookDAOImpl(接口实现类) Myproxy(代理类)
两个以上的话 代理类封装接口-->提供有参构造方法--> 通过代理可以两个类切换
使用代理的好处
高内聚 低耦合
动态代理,几乎可以为所有的类产生代理对象 动态代理的实现方式有2种: JDK动态代理 CGLIB动态代理
动态代理,几乎可以为所有的类产生代理对象
动态代理的实现方式有2种:
package com.dao; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * @author WanShen * @date 2022年03月07日 11:07 PM * JDK动态代理:是通过被代理对象实现的接口产生其代理对象的 * 1.创建一个类,实现InvocationHandler 重写invoke方法 * 2.在类中定义一个Object类型的变量,用于传递被代理对象 并提供这个对象的有参构造器 * 用于将被代理的类传递进来 * 3.定义getProxy()方法 用于创建返回对象 * 4.重写invoke方法; */ public class JDKDynamicProxy implements InvocationHandler { // 被代理对象 private Object object; public JDKDynamicProxy(Object object) { this.object = object; } // 产生代理对象,返回代理对象 public Object getProxy() { // 1.获取被代理对象的类加载器 ClassLoader classLoader = object.getClass().getClassLoader(); // 2.获取被代理对象的类实现的接口 Class<?>[] interfaces = object.getClass().getInterfaces(); // 3.产生代理对象(通过被代理对象的类加载器以及实现的接口) // 第一个参数:被代理对象的类加载器 // 第二个参数:被代理对象实现的接口 // 第三个参数:使用产生的代理对象调用方法时用于拦截方法执行时的处理器 Object proxy = Proxy.newProxyInstance(classLoader, interfaces, this); return proxy; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object returnValue = null; try { // 方法执行前 begin(); returnValue = method.invoke(object, args); // 方法执行后 commit(); } catch (Exception e) { e.printStackTrace(); // 发生异常后 rollback(); } return returnValue; } public void begin() { System.out.println("--开启事务"); } public void commit() { System.out.println("--提交事物"); } public void rollback() { System.out.println("--回滚事务"); }
// 被代理对象 BookDapImpl bookDao = new BookDapImpl(); // 创建动态代理类对象,并将代理类对象传递到代理类中赋值给obj JDKDynamicProxy jdkDynamicProxy = new JDKDynamicProxy(bookDao); // proxy就是产生的代理对象:可以强转成被代理对象实现的接口类型 GeneralDAO proxy = (GeneralDAO) jdkDynamicProxy.getProxy(); // 使用代理对象调用方法,并不会执行调用的方法,而是进入到创建代理对象时指定的InvocationHandler类中的invoke方法 // 调用的方法作为一个Method参数给了invoke方法 proxy.delete();
限制:只能为实现了接口的类产生代理;
由于JDK动态代理是通过被代理类实现的接口来创建代理对象的,因此JDK动态代理只能代理实现了接口的类的对象.如果一个类没有任何实现接口该如何产生代理对象呢? CGLib动态代理,是通过创建被代理类的子类来创建代理对象的,因此 即使没有实现任何接口的类也可以通过CGLib产生代理对象,CGLib动态代理 不能为final类创建代理对象;
由于JDK动态代理是通过被代理类实现的接口来创建代理对象的,因此JDK动态代理只能代理实现了接口的类的对象.如果一个类没有任何实现接口该如何产生代理对象呢?
CGLib动态代理,是通过创建被代理类的子类来创建代理对象的,因此 即使没有实现任何接口的类也可以通过CGLib产生代理对象,CGLib动态代理 不能为final类创建代理对象;
<!-- https://mvnrepository.com/artifact/cglib/cglib --> <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.3.0</version> </dependency>
package com.dao; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method; /** * @author WanShen * @date 2022年03月08日 12:36 PM * 1.添加CGLib依赖 * 2.创建一个类,实现MethodInterceptor接口 同时实现interceptor方法 * 3.在类中定义一个Object类型的变量 并提供这个变量的有参构造器,用于传递被代理对象 * 4.定义getProxy方法创建并返回代理对象(代理对象是通过创建被代理类的子类来创建的) */ public class CGLibDynamicProxy implements MethodInterceptor { private Object object; public CGLibDynamicProxy(Object object) { this.object = object; } public Object getProxy(){ Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(object.getClass()); enhancer.setCallback(this); Object proxy = enhancer.create(); return proxy; } @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { begin(); Object returnValue = method.invoke(object,objects); commit(); return returnValue; } public void begin(){ System.out.println("--开启事务"); } public void commit(){ System.out.println("--提交事物"); } }
// 被代理对象 BookDapImpl bookDao = new BookDapImpl(); //通过CGLib动态代理类创建代理对象 CGLibDynamicProxy cgLibDynamicProxy = new CGLibDynamicProxy(new BookDapImpl()); //代理对象实际上是被代理对象的子类,因此代理对象可以直接强转为被代理类类型 BookDapImpl proxy = (BookDapImpl) cgLibDynamicProxy.getProxy(); //使用对象调用方法,实际上并没有执行这个方法,而是执行了代理类中的intercept方法 将当前调用的方法的参数传递到intercept方法 proxy.delete();
Aspect Oriented Programming面向切面编程,是一种利用“横切”的技术(底层实现就是动态代理) 对原有的业务逻辑进行拦截,并且可以在这个拦截的横切面上添加特定的业务逻辑,对原有的业务进行增强。 基于动态代理实现在不改变原有业务的情况下对业务逻辑进行增强
Aspect Oriented Programming面向切面编程,是一种利用“横切”的技术(底层实现就是动态代理) 对原有的业务逻辑进行拦截,并且可以在这个拦截的横切面上添加特定的业务逻辑,对原有的业务进行增强。
基于动态代理实现在不改变原有业务的情况下对业务逻辑进行增强
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.13.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>5.2.13.RELEASE</version> </dependency>
<?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"> </beans>
在DAO的方法添加开启事物和提交事物的逻辑
package com.utils; /** * @author WanShen * @date 2022年03月08日 5:42 PM */ public class TxManger { public void begin(){ System.out.println("开启事务"); } public void commit(){ System.out.println("提交食物"); } }
<aop:pointcut id="book_all" expression="execution(* com.dao.*.*(..))"/> execution(* com.dao.*.*(..)) 第一个 * 是 所有返回类型 第二个 * 是com.dao下所有的类的所有方法
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <bean class="com.dao.BookDapImpl" id="bookDap"></bean> <bean id="txManger" class="com.utils.TxManger"></bean> <aop:config> <!--声明切入点--> <aop:pointcut id="book_all" expression="execution(* com.dao.*.*(..))"/> <!--声明txManger为切面类--> <aop:aspect ref="txManger"> <!--通知--> <aop:before method="begin" pointcut-ref="book_all" /> <aop:after method="commit" pointcut-ref="book_all" /> </aop:aspect> </aop:config> </beans>
AOP开发步骤:
<!--使用aop:pointcut标签声明切入点: 切入点可以是一个方法--> <aop:pointcut id="book_pc1" expression="execution(* com.dao.BookDapImpl.insert())"/> <!--BookDapImpl类中 类中所有无参数无返回值的方法--> <aop:pointcut id="book_pc2" expression="execution(void com.dao.BookDapImpl.*())"/> <!--BookDapImpl类 中所有无返回值的方法--> <aop:pointcut id="book_pc3" expression="execution(void com.dao.BookDapImpl.*(..))"/> <!--BookDapImpl类 中所有无参数的方法--> <aop:pointcut id="book_pc4" expression="execution(* com.dao.BookDapImpl.*())"/> <!--BookDapImpl类 中的所有方法--> <aop:pointcut id="pc5" expression="execution(* com.dao.BookDapImpl.*(..))"/> <!--dao包中所有类的所有方法--> <aop:pointcut id="pc6" expression="execution(* com.dao.*.*(..))"/> <!--所有包中所有类的所有方法--> <aop:pointcut id="pc7" expression="execution(* *(..))"/>
如果要使用Spring aop切面编程,调用切入点方法的对象必须通过Spring容器获取 如果一个类中的方法被声明为切入点之后, 通过spring容器获取对象 实则获取到的是一个代理对象。 如果一个类中的方法没有被声明为切入点,通过Spring容器获取的就是这个类真实创建的对象。
如果要使用Spring aop切面编程,调用切入点方法的对象必须通过Spring容器获取
如果一个类中的方法被声明为切入点之后, 通过spring容器获取对象 实则获取到的是一个代理对象。
如果一个类中的方法没有被声明为切入点,通过Spring容器获取的就是这个类真实创建的对象。
AOP通知策略: 就是声明将切面类中的切点方法如何织入到切入点
package com.utils; /** * @author WanShen * @date 2022年03月08日 5:42 PM */ public class TxManger { public void begin(){ System.out.println("开启事务"); } public void commit(){ System.out.println("提交事物"); } }
<aop:config> <!--所有包中所有类的所有方法--> <aop:pointcut id="pc7" expression="execution(* *(..))"/> <!--声明txManger为切面类--> <aop:aspect ref="txManger"> <!--aop:before 前置通知 切入到指定切点之前--> <aop:before method="begin" pointcut-ref="pc7" /> <!--aop:before 后置通知 切入到指定切点之后--> <aop:after method="begin" pointcut-ref="pc7" /> <!--aop:after-throwing 异常通知 切入点抛出异常之后--> <aop:after-throwing method="begin" pointcut-ref="pc7" /> <!--aop:after-returning 方法返回值之后,对于一个java方法而言 return返回值也是方法的一部分因此"方法返回值返回之后"和"方法执行结束" 是同一个时间点,所以after和after-returning根据配置的顺序决定执行顺序 --> <aop:after-returning method="begin" pointcut-ref="pc7" /> <!--aop:around 环绕通知--> <aop:around method="begin" pointcut-ref="pc7" /> </aop:aspect> </aop:config>
<?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:annotation-config/> <!-- 声明Spring工厂注解的扫描范围--> <context:component-scan base-package="com.dao"/> <!--基于注解配置的AOP代理--> <aop:aspectj-autoproxy ></aop:aspectj-autoproxy> </beans>
package com.utils; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.springframework.stereotype.Component; /** * @author WanShen * @date 2022年03月09日 1:42 PM */ @Component //声明Spring容器管理此类 @Aspect //声明此类是切面类 public class TransactionManager { @Pointcut("execution(* com.dao.*.*(..))") public void Pointcut(){} @Before("Pointcut()") public void begin() { System.out.println("开启事物"); } @After("Pointcut()") public void commit() { System.out.println("提交事物"); } @Around("Pointcut()") public Object printExecudate(ProceedingJoinPoint point) throws Throwable { long tim1 = System.currentTimeMillis(); Object v = point.proceed(); long tim2 = System.currentTimeMillis(); System.out.println("执行时间:"+(tim2-tim1)); return v; } }
注意:注解使用虽然方便 但只能在源码上添加注解,因此我们的自定义提倡使用注解配置;如果使用到第三方提供的类则需要通过xml配置形式完成配置。
注意:
Spring 量大核心思想: ioc和aop ioc: 控制反转, Spring容器可以完成对象的创建、属性注入、对象管理等工作 AOP: 面向切面, 在不修改原有业务逻辑的情况下,实现对原有业务的增强
Spring 量大核心思想: ioc和aop
ioc: 控制反转, Spring容器可以完成对象的创建、属性注入、对象管理等工作
AOP: 面向切面, 在不修改原有业务逻辑的情况下,实现对原有业务的增强
IOC支持:SpringioC可以为Mybatis完成DataSource、SqlSessionFactory、SqlSession以及DAO对象的创建
AOP支持使用Spring提供的事物管理切面类完成对MyBatis数据库操作中的事物管理
<!--mybatis--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.2</version> </dependency> <!--mysql--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> </dependency>
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd" > <configuration> </configuration>
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.13.RELEASE</version> </dependency> <!--切面 aop的支持--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>5.2.13.R ELEASE</version> </dependency> <!--Spring对持久层的支持--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.2.13.RELEASE</version> </dependency>
<?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"> </beans>
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>1.3.2</version> </dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba/druid --> <!--druid连接池依赖--> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.2.5</version> </dependency>
druid.driver=com.mysql.Driver druid.url=jdbc:mysql://localhost:3306/date_demo?useUnicode=true&characterEncoding=utf-8&useSSL=false; druid.username=root druid.password=admin # 连接池参数 druid.pool.init=1 druid.pool.minIdle=3 druid.pool.maxActive=20 druid.pool.timeout=30000
<?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"> <!--加载druid.properties属性文件--> <context:property-placeholder location="classpath:druid.properties" /> <bean id="driver" class="com.mysql.jdbc.Driver"/> <!--依赖Spring容器完成对DataSource的创建--> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driver" ref="driver"/> <property name="url" value="${druid.url}" /> <property name="username" value="${druid.username}" /> <property name="password" value="${druid.password}" /> <property name="initialSize" value="${druid.pool.init}" /> <property name="minIdle" value="${druid.pool.minIdle}" /> <property name="maxActive" value="${druid.pool.maxActive}" /> <property name="maxWait" value="${druid.pool.timeout}" /> </bean> </beans>
<!--依赖Spring容器完成MyBatis的SqlSessionFactory对象创建--> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean" autowire="byName"> <!--配置数据源--> <!-- <property name="dataSource" ref="dataSource" />--> <!--配置Mapper文件的位置/路径--> <!-- <property name="mapperLocations" value="classpath:mapper/*Mapper.xml" />--> <!--配置需要定义别名的实体类的包--> <property name="typeAliasesPackage" value="com.wanshen.pojo"/> <!--可选:配置Mybatis的主配置文件--> <property name="configLocation" value="classpath:mybatis-config.xml"/> </bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" /> <property name="basePackage" value="com.wanshen.dao" /> </bean>
使用Spring提供的事物管理切面类 完成DAO中增删改操作的事物管理
外层实物的概念解释: 一个比较复杂的业务中,往往会出现在一个service方法中,调用其他的service方法(可能是同一类也可能是其他类的service方法) 某个service方法中 s1 s2 s3需要调用s1 s2 那么s3本身就是service中的方法 有事务 s1 s2也有事务 S3相对于S1 s2来讲就是 外层事物
外层实物的概念解释:
一个比较复杂的业务中,往往会出现在一个service方法中,调用其他的service方法(可能是同一类也可能是其他类的service方法)
某个service方法中 s1 s2 s3需要调用s1 s2 那么s3本身就是service中的方法 有事务 s1 s2也有事务
S3相对于S1 s2来讲就是 外层事物
不可重复度=>一方更新导致两次查询结果不一致
isolation 设置事务隔离级别: READ_UNCOMMITTED、READ_COMMITTED、REPEATABLE_READ、SERIALIZABLE READ_UNCOMMITTED: 读 未提交-> T1在执行的过程中 T2既可以读也可以写 T1可以提取到T2未提交的数据(脏读)、(不可重复读)、(幻读) READ_COMMITTED: 读 已提交 -> T1在执行的过程中T2可以读也可以写但是T1只能读到T2提交后的数据(不可重复读)、(幻读) REPEATABLE_READ: 可重复读 -> T1 在执行的过程中T2只能读但不能写 T2可以添加数据(幻读) SERIALIZABLE:串行化/序列化-> T1 在执行的过程中T2既不能读也不能写
isolation 设置事务隔离级别: READ_UNCOMMITTED、READ_COMMITTED、REPEATABLE_READ、SERIALIZABLE
READ_UNCOMMITTED: 读 未提交-> T1在执行的过程中 T2既可以读也可以写 T1可以提取到T2未提交的数据(脏读)、(不可重复读)、(幻读)
READ_COMMITTED: 读 已提交 -> T1在执行的过程中T2可以读也可以写但是T1只能读到T2提交后的数据(不可重复读)、(幻读)
REPEATABLE_READ: 可重复读 -> T1 在执行的过程中T2只能读但不能写 T2可以添加数据(幻读)
SERIALIZABLE:串行化/序列化-> T1 在执行的过程中T2既不能读也不能写
事务的隔离级别越高 会导致事务的安全性高 但是效率降低
propagation 事务的传播机制: REQUIRED 如果上层方法没有事务,则创建一个新的事务 如果已存在事务则加入到事务中. SUPPORTS 如果上层方法没有事务,则以非事务方式执行 如果已存在事务则加入到事务中. REQUIRES_NEW 如果上层方法没有事务,则创建一个新的事务 如果已经存在事务,则将当前事务挂起. NOT_SUPPORTED 如果上层方法没有事务,则以非事务方式执行 如果已存在事务则将当前事务挂起. NEVER 如果上层方法没有事务,则以非事务方式执行 如果已存在事务则抛出异常. MANDATORY 如果上层方法已经存在事务,则加入到事务中执行,如果不存在事务则抛出异常 NESTED 如果上层方法没有事务,则创建一个新的事务 如果已存在事务则嵌套到当前事务中.
一般 增删改用REQUIRED 查询用SUPPORTS 日志类用 REQUIRES_NEW
<!--1.将Spring提供的事务管理类配置给Spring容器--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <!--2.通过Spring提供的tx标签 声明事务管理策略--> <tx:advice id="txadvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="insert" isolation="REPEATABLE_READ" propagation="REQUIRED"/> <tx:method name="delete" isolation="REPEATABLE_READ" propagation="REQUIRED"/> <tx:method name="update" isolation="REPEATABLE_READ" propagation="REQUIRED"/> <tx:method name="query" isolation="REPEATABLE_READ" propagation="NOT_SUPPORTED"/> </tx:attributes> </tx:advice> <aop:config> <aop:pointcut id="crud" expression="execution(* com.wanshen.dao.*.*(..))"/> <aop:advisor advice-ref="txadvice" pointcut-ref="crud" /> </aop:config>
⚠️注意: 事务在dao中不明智 应在service中 因为一个service中包含多个dao的操作
应该为:
<aop:pointcut id="crud" expression="execution(* com.wanshen.service.*.*(..))"/>
<!--1.将Spring提供的事务管理类配置给Spring容器--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <!--2.声明使用注解完成事务配置--> <tx:annotation-driven transaction-manager="transactionManager" />
@Transactional
package com.wanshen.service; import com.wanshen.dao.UserDao; import com.wanshen.pojo.User; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import javax.annotation.Resource; import java.util.List; /** * @author WanShen * @date 2022年03月10日 7:21 PM */ @Service public class UserServiceImpl implements UserService { @Resource private UserDao userDao; @Override @Transactional(isolation = Isolation.REPEATABLE_READ,propagation = Propagation.SUPPORTS) public List<User> listUsers() { return userDao.queryUsers(); } }
log4j(log for java)Java的日志框架。
日志级别:OFF、FATAL、ERROR、WARN、INFO、DEBUG、ALL (从高到低),级别越高,输出的日志越少(比如设置为WARN时,是输出WARN、ERROR和FATAL级别)。
日志分类:根日志(rootLogger,全局日志)、分支日志(logger,包级别的日志),其中根日志必须存在。
引入依赖
<dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.25</version> </dependency>
配置(必须是src/main/resources/log4j.properties),不需要会写这个配置,能看懂和修改即可
# 根日志级别ERROR,输出到stdout log4j.rootLogger=ERROR,stdout # 设置stdout的输出使用ConsoleAppender(控制台) log4j.appender.stdout=org.apache.log4j.ConsoleAppender # 设置stdout的显示方式为PatternLayout(自定义格式) log4j.appender.stdout.layout=org.apache.log4j.PatternLayout # 设置stdout的格式 %p为日志级别 %t为线程名 %d为日期{格式} %m为主线程日志 %n为换行 log4j.appender.stdout.layout.ConversionPattern=%p [%t] %d{yyyy-MM-dd HH:mm:ss} - %m%n # 设置com.baizhi.dao包的日志级别为DEBUG,用来输出SQL语句 log4j.logger.com.baizhi.dao=DEBUG # 推荐开发项目 根级别日志为ERROR # 输出自己代码中所有的日志信息 log4j.logger.com.baizhi = DEBUG # 把dao所在的子包设置为ERROR,因为SQL语句帮助不是很大 log4j.logger.com.baizhi.dao = ERROR
自己使用日志对象来输出日志
//成员变量中 //参数一般为当前类的类对象 private static final Logger LOGGER = LoggerFactory.getLogger(UserServiceImpl.class); //想输出日志的地方 LOGGER.debug("debug"); LOGGER.info("info"); LOGGER.warn("warn"); LOGGER.error("error"); //也可以采用{}占位符输出形式 LOGGER.info("name: {}, id: {}", user.getName(), user.getId());
加密,是以某种特殊的算法改变原有的信息数据,使得未授权的用户即使获得了已加密的信息,但因不知解密的方法,仍然无法了解信息的内容。
信息在加密之前称为明文,在加密之后称为密文。
严格来说,MD5算法的主要作用不是加密,而是生成数据特征码。这个特征码有以下两个性质:
因为数据的种类远远超过特征码能表示的种类,因此存在特征码相同时,数据不同的情况。但实际上重复的概率极低。
MD5加密的缺点:如果对比较短的数据进行加密,会很容易破解。
对原始数据(明文)前面或后面拼接一段数据,使得明文比较长,在经过MD5加密之后变得不容易破解。这段被用来拼接的数据就叫做盐。
java代码:
String str = "test"; String md5 = DigestUtils.md5DigestAsHex(str.getBytes());
public static String getSalt(){ return getSalt(20); } public static String getSalt(int size){ char[] pool = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".toCharArray(); StringBuilder sb = new StringBuilder(); Random random = new Random(); for(int i = 0;i < size;i ++){ sb.append(pool[random.nextInt(pool.length)]); } return sb.toString(); } public static String getPassword(String pass, String salt){ pass = pass + salt; return DigestUtils.md5DigestAsHex(pass.getBytes()); }
数据库的事务有ACID特性:
在spring依赖的基础上,加入以下依赖:
<dependency><!--用来控制事务--> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>4.3.2.RELEASE</version> </dependency> <dependency><!--mybatis--> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.2.8</version> </dependency> <dependency><!--mybatis结合spring使用的依赖--> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>1.3.1</version> </dependency> <dependency><!--mysql数据库驱动--> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.40</version> </dependency> <dependency><!--阿里巴巴德鲁伊连接池--> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.12</version> </dependency>
建表
CREATE TABLE `t_user` ( `id` varchar(40) NOT NULL, `name` varchar(40) DEFAULT NULL, `age` int(3) DEFAULT NULL, `bir` datetime NULL DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
实体类
public class User implements Serializable{ private String id; private String name; private Integer age; private Date bir; //构造、getter/setter、toString等 }
dao接口
package com.baizhi.dao; public interface UserDAO { List<User> findAll(); void save(User user); }
mapper
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace="com.baizhi.dao.UserDao"> <select id="findAll" resultType="com.baizhi.entity.User"> SELECT id,name,age,bir FROM t_user </select> <insert id="save" parameterType="com.baizhi.entity.User"> INSERT INTO t_user (id, `name`, age, bir) VALUES (#{id}, #{name}, #{age}, #{bir}) </insert> </mapper>
mybatis-spring官方整合中不再需要mybatis主配置文件。
<!--引入db小配置文件--> <context:property-placeholder location="classpath:db.properties"/> <!--创建数据源对象--> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="${mysql.driver}"/> <property name="url" value="${mysql.url}"/> <property name="username" value="${mysql.username}"/> <property name="password" value="${mysql.password}"/> </bean> <!--创建sqlSessionFactory--> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <!--注入DataSource对象--> <property name="dataSource" ref="dataSource"/> <!--注入Mapper文件位置--> <property name="mapperLocations" value="classpath:com/baizhi/mapper/*.xml"/> <!--实体类别名(非必须,建议不使用这个而在Mapper中实体类写上类的全限定名)--> <property name="typeAliasesPackage" value="com.baizhi.entity"/> </bean> <!--自动注册DAO,自动注册的id为接口首字母小写--> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <!--sqlSessionFactoryBean的bean名字--> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/> <!--所有dao接口所在的包名--> <property name="basePackage" value="com.baizhi.dao"/> </bean>
ApplicationContext ctx = new ClassPathXmlApplicationContext("spring.xml"); UserDAO userDao = (UserDAO) ctx.getBean("userDao"); List<User> users = userDao.findAll(); System.out.println(users);
1.Service接口
public interface UserService { List<User> findAll(); void save(User user); }
2.Service实现类
public class UserServiceImpl implements UserService { private UserDAO userDAO; public void setUserDAO(UserDAO userDAO) { this.userDAO = userDAO; } @Override public List<User> findAll() { return userDAO.findAll(); } @Override public void save(User user) { userDao.save(user); } }
3.在配置文件中添加
<!--创建事务管理器--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <!--配置事务属性,这相当于创建了一个专门完成事务控制的通知对象,这个id就是通知对象的id--> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="save*"/> <tx:method name="update*"/> <tx:method name="delete*"/> <tx:method name="find*" propagation="SUPPORTS"/> <tx:method name="log*" propagation="REQUIRES_NEW"/> </tx:attributes> </tx:advice> <!--配置事务切面--> <aop:config> <aop:pointcut id="pc" expression="within(com.baizhi.service.*ServiceImpl)"/> <aop:advisor advice-ref="txAdvice" pointcut-ref="pc"/> </aop:config> <!--注册所有的Service组件--> <bean id="userService" class="com.baizhi.service.UserServiceImpl" autowire="byType"/>
事务属性特别说明: 增删改使用默认事务属性、查询使用propagation="SUPPORTS"、日志相关使用propagation="REQUIRES_NEW"是常见的实现,这并不一定符合所有的业务需要。
事务属性特别说明:
增删改使用默认事务属性、查询使用propagation="SUPPORTS"、日志相关使用propagation="REQUIRES_NEW"是常见的实现,这并不一定符合所有的业务需要。
propagation="SUPPORTS"
propagation="REQUIRES_NEW"
4.启动工厂测试
ApplicationContext ctx = new ClassPathXmlApplicationContext("spring.xml"); UserService userService = (UserService) ctx.getBean("userService"); User user = new User(); user.setName("test1"); userService.save(user);
<!--struts2--> <dependency> <groupId>org.apache.struts</groupId> <artifactId>struts2-core</artifactId> <version>2.3.16</version> </dependency> <!--javaee--> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.5</version> <scope>provided</scope> </dependency> <!--jstl--> <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency> <!--struts2-spring-plugin 版本号和struts2-core要一致--> <dependency> <groupId>org.apache.struts</groupId> <artifactId>struts2-spring-plugin</artifactId> <version>2.3.16</version> </dependency>
<!--设置spring配置文件的位置--> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring.xml</param-value> </context-param> <filter> <filter-name>struts2</filter-name> <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class> </filter> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!--在项目启动时自动启动spring工厂--> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>
public class UserAction extends ActionSupport { public String hello(){ System.out.println("action: hello"); return ActionSupport.SUCCESS; } }
所有的Action都需要使用工厂创建
<!--配置Action的bean,scope需要为prototype--> <bean id="userAction" class="com.baizhi.action.UserAction" scope="prototype"/>
<package name="hello" extends="struts-default" namespace="/hello"> <!--class为工厂中的名字--> <action name="hello" class="userAction" method="hello"> <result name="success">/index.jsp</result> </action> </package>
复杂对象:类中没有构造方法或者构造方法不能调用,如接口类型或抽象类实例。
package com.baizhi.factorybean; public class ConnectionFactoryBean implements FactoryBean<Connection> { @Override //用来指定复杂对象的创建方式(返回创建好的对象) public Connection getObject() throws Exception { Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/ajax", "root", "1234"); return connection; } @Override //用来指定复杂对象的类型(返回指定复杂对象的类对象) public Class<?> getObjectType() { return Connection.class; } @Override //用来指定复杂对象的创建次数(返回是否是单例) public boolean isSingleton() { return false; } }
<bean id="connection" class="com.baizhi.factorybean.ConnectionFactoryBean"/>
spring.xml中手动注册所有的service实现类和action类,在service bean上注入dao,在action bean上注入service。例如:
<!--注册所有service--> <bean class="com.baizhi.service.UserServiceImpl" id="userService" autowire="byType"/> <bean class="com.baizhi.service.CityServiceImpl" id="cityService" autowire="byType"/> <bean class="com.baizhi.service.AdminServiceImpl" id="adminService" autowire="byType"/> <!--注册所有action,别忘了scope="prototype",根据情况,自动注入时也可能是byName--> <bean class="com.baizhi.action.UserAction" id="userAction" scope="prototype" autowire="byType"/> <bean class="com.baizhi.action.CityAction" id="cityAction" scope="prototype" autowire="byType"/> <bean class="com.baizhi.action.AdminAction" id="adminAction" scope="prototype" autowire="byType"/>
所有service实现类中使用到的所有dao都是成员属性,且提供setter方法,不能通过直接通过getMapper()等形式进行自身管理;所有action类中的使用到的所有service都是成员属性,且提供setter方法,不能直接通过new XxxServiceImpl()等形式进行自身管理。例如:
getMapper()
new XxxServiceImpl()
public class UserServiceImpl implements UserService{ private UserDao userDao; private CityDao cityDao; public void setUserDao(UserDao userDao) { this.userDao = userDao; } public void setCityDao(CityDao cityDao) { this.cityDao = cityDao; } } public class UserAction{ private UserService userService; private CityService cityService; public void setUserService(UserService userService) { this.userService = userService; } public void setCityService(CityService cityService) { this.cityService = cityService; } }
事务属性name写成save*、update*、delete*、log*、find*并不是固定代码,这么写的前提是:
save*
update*
delete*
log*
find*
save
update
delete
log
find
struts.xml中的class不要写全限定名而是写action对象在Spring工厂中的id(name)。
struts.xml
不要忘了在配置文件中加入<context:component-scan base-package="com.baizhi"/>
<context:component-scan base-package="com.baizhi"/>
不要忘了在配置文件中加入<tx:annotation-driven transaction-manager="transactionManager"/>,同时,不用再配置事务属性和配置事务切面。
<tx:annotation-driven transaction-manager="transactionManager"/>
所有service实现类上都使用@Service注册自身到工厂;并使用@Transactional注解开启事务,同时,当前类中不是采用默认事务属性的service方法需要单独设置@Transactional的事务属性,比如查询类方法@Transactional(propagation = Propagation.SUPPORTS)等;其中使用到的所有dao都是成员属性,且使用@Autowired进行自动注入。例如:
@Service
@Transactional(propagation = Propagation.SUPPORTS)
@Service @Transactional public class UserServiceImpl implements UserService{ @Autowired private UserDao userDao; @Autowired private CityDao cityDao; @Override public void save(User user) { userDao.insert(user); } @Override @Transactional(propagation = Propagation.SUPPORTS) public List<User> findAll() { return userDao.selectAll(); } }
所有action类上都使用@Controller注册自身到工厂;使用@Scope("prototype")声明为非单例;struts.xml中的class不要写全限定名而是写action对象在工厂中的id(name);其中使用到的所有service都是成员属性,且使用@Autowired或@Resource进行自动注入。例如:
@Controller
@Scope("prototype")
@Controller @Scope("prototype") public class UserAction { @Autowired private UserService userService; @Autowired private CityService cityService; }
<package name="user" extends="struts-default" namespace="/user"> <!--class为工厂中的id--> <!--使用@Controller注册的对象,默认id为类名首字母小写--> <action name="findAll" class="userAction" method="findAll"> <result name="success">/index.jsp</result> </action> </package>
本文来自博客园,作者:万神·,转载请注明原文链接:https://www.cnblogs.com/wanshen/p/16841144.html