Java SSM4——Spring
Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器(框架)
Spring的优势
- 方便解耦,简化开发
- Spring就是一个容器,可以将所有对象创建和关系维护交给Spring管理 什么是耦合度?对象之间的关系,通常说当一个模块(对象)更改时也需要更改其他模块(对象),这就是耦合,耦合度过高会使代码的维护成本增加。要尽量解耦
- AOP编程的支持
- Spring提供面向切面编程,方便实现程序进行权限拦截,运行监控等功能
- 声明式事务的支持
- 通过配置完成事务的管理,无需手动编程
- 方便测试,降低JavaEE API的使用
- Spring对Junit4支持,可以使用注解测试
- 方便集成各种优秀框架
- 不排除各种优秀的开源框架,内部提供了对各种优秀框架的直接支持
1、IOC
Inverse Of Control:控制反转:配置文件(解除硬编码)+反射(解除编译器依赖)
- 控制:在java中指的是对象的控制权限(创建、销毁)
- 反转:指的是对象控制权由原来 由开发者在类中手动控制交由Spring管理
2、AOP
Aspect Oriented Programming:面向切面编程:动态代理(方法增强)
3、快速入门
默认为maven项目
3.1、导入依赖
<dependencies><!--spring--><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.3.9</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.13</version><scope>test</scope></dependency>
</dependencies>
3.2、Spring核心配置文件
如无特殊需求,我们默认spring核心配置文件名为applicationContent.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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="HelloSpring" class="club.winkto.bean.HelloSpring"></bean>
</beans>
3.3、书写简单的类
public class HelloSpring {public void hellospring(){System.out.println("hello spring!");}
}
3.4、测试
@Test
public void test(){ClassPathXmlApplicationContext classPathXmlApplicationContext =new ClassPathXmlApplicationContext("applicationContent.xml");HelloSpring hellospring = classPathXmlApplicationContext.getBean("hellospring", HelloSpring.class);hellospring.hellospring();
}
4、Spring API
BeanFactory也可以完成ApplicationContext完成的事情,为什么我们要选择ApplicationContext?
BeanFactory:在第一次调用getBean()方法时,创建指定对象的实例
ApplicationContext:在spring容器启动时,加载并创建所有对象的实例
4.1、常用实现类
- ClassPathXmlApplicationContext 它是从类的根路径下加载配置文件 推荐使用这种
- FileSystemXmlApplicationContext 它是从磁盘路径上加载配置文件,配置文件可以在磁盘的任意位置
- AnnotationConfigApplicationContext 当使用注解配置容器对象时,需要使用此类来创建 spring 容器,它用来读取注解
5、Spring IOC
5.1、bean
5.1.1、基础使用
<bean id="" class=""></bean>
- id:Bean实例在Spring容器中的唯一标识
- class:Bean的全限定名
- 默认情况下它调用的是类中的 无参构造函数,如果没有无参构造函数则不能创建成功
5.1.2、scope属性
<bean id="" class="" scope=""></bean>
- singleton:单例(默认)
- 对象创建:当应用加载,创建容器时,对象就被创建了
- 对象运行:只要容器在,对象一直活着
- 对象销毁:当应用卸载,销毁容器时,对象就被销毁了
- prototype:多例
- 对象创建:当使用对象时,创建新的对象实例
- 对象运行:只要对象在使用中,就一直活着
- 对象销毁:当对象长时间不用时,被 Java 的垃圾回收器回收了
5.1.3、生命周期的配置
<bean id="" class="包名.类名" init-method="指定class的方法" destroy-method="指定class的方法"></bean>
- init-method:指定类中的初始化方法名称
- destroy-method:指定类中销毁方法名称
5.1.4、Bean实例化的三种方式
5.1.4.1、构造器实例化
spring容器通过bean对应的默认的构造函数来实例化bean。
上述即是
5.1.4.2、 静态工厂方式实例化
<bean id="" class="包名.类名" factory-method="指定class的方法" />
首先创建一个静态工厂类,在类中定义一个静态方法创建实例。
静态工厂类及静态方法:
public class MyUserDaoFactory{//静态方法,返回UserDaoImpl的实例对象public static UserDaoImpl createUserDao{return new UserDaoImpl();}
}
xml配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN""http://www.springframework.org/dtd/spring-beans.dtd">
<beans><!-- 将指定对象配置给spring,让spring创建其实例 --><bean id="userDao" class="com.ioc.MyUserDaoFactory" factory-method="createUserDao"/>
</beans>
5.1.4.3、 实例工厂方式实例化
<bean id="工厂类id" class="包名.类名"/>
<bean id="" factory-bean="工厂类id" factorymethod="工厂类id里的方法"/>
该种方式的工厂类中,不再使用静态方法创建Bean实例,而是采用直接创建Bean实例的方式。同时在配置文件中,需要实例化的Bean也不是通过class属性直接指向其实例化的类,而是通过factory-bean属性配置一个实例工厂,然后使用factory-method属性确定使用工厂中哪个方法。
工厂类方法:
public class MyBeanFactory{public MyBeanFactory(){System.out.println("this is a bean factory");}public UserDaoImpl createUserDao(){return new UserDaoImpl();}
}
xml配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN""http://www.springframework.org/dtd/spring-beans.dtd">
<beans><!-- 配置工厂 --><bean id="myBeanFactory" class="com.ioc.MyBeanFactory"/><!-- 使用factory-bean属性配置一个实例工厂,使用factory-method属性确定工厂中的哪个方法 --><bean id="userDao" factory-bean="myBeanFactory" factory-method="createUserDao"/>
</beans>
主函数
public class Client {public static void main(String[] args) {// TODO Auto-generated method stub//此处定义xml文件放置的位置为src目录下的com/xml目录下String path = "com/xml/bean.xml";ApplicationContext application = new ClassPathXmlApplicationContext(path);UserDaoImpl userDao = (UserDaoImpl) application.getBean("userDao");userDao.sayHello(); //调用UserDaoImpl类的sayHello方法}
}
5.1.5、别名
5.1.5.1、alias标签
<alias name="hello" alias="hello1"></alias>
5.1.5.2、bean标签name属性
<bean id="HelloSpring" name="hello2 h2,h3;h4" class="club.winkto.bean.HelloSpring"></bean>
可写多个别名,分隔符为空格,逗号,分号均可
5.2、依赖注入(DI)
Spring 框架核心 IOC 的具体实现
5.2.1、构造方法
mapper
public interface WinktoMapper {void mapper();
}
public class WinktoMapperImpl implements WinktoMapper {public void mapper() {System.out.println("假装自己有了数据库查询");}
}
service
public interface WinktoService {void service();
}
public class WinktoServiceImpl implements WinktoService {private int a;private WinktoMapperImpl winktoMapper;private Object[] arrays;private List list;private Map<String,Object> map;private Set set;private Properties properties;public WinktoServiceImpl(int a, WinktoMapperImpl winktoMapper, Object[] arrays, List list, Map<String, Object> map, Set set, Properties properties) {this.a = a;this.winktoMapper = winktoMapper;this.arrays = arrays;this.list = list;this.map = map;this.set = set;this.properties = properties;}public void service() {System.out.println("假装自己开启了事务");winktoMapper.mapper();System.out.println("=================");System.out.println(toString());System.out.println("=================");System.out.println("假装自己提交了事务");}@Overridepublic String toString() {return "WinktoServiceImpl{" +"a=" + a +", winktoMapper=" + winktoMapper +", arrays=" + arrays +", list=" + list +", map=" + map +", set=" + set +", properties=" + properties +'}';}
}
aplicationContent.xml
<bean id="winktoMapper" class="club.winkto.mapper.WinktoMapperImpl"></bean><bean id="winktoService" class="club.winkto.service.WinktoServiceImpl"><!--<constructor-arg index="0" value="12"></constructor-arg>--><!--<constructor-arg index="1" ref="winktoMapper"></constructor-arg>--><!--<constructor-arg index="2">--><!-- <array>--><!-- <value>1</value>--><!-- <ref bean="winktoMapper"></ref>--><!-- </array>--><!--</constructor-arg>--><!--<constructor-arg index="3">--><!-- <list>--><!-- <value>2</value>--><!-- <ref bean="winktoMapper"></ref>--><!-- </list>--><!--</constructor-arg>--><!--<constructor-arg index="4">--><!-- <map>--><!-- <entry key="name" value="zhangsan"></entry>--><!-- <entry key="service" value-ref="winktoMapper"></entry>--><!-- </map>--><!--</constructor-arg>--><!--<constructor-arg index="5">--><!-- <set>--><!-- <value>3</value>--><!-- <ref bean="winktoMapper"></ref>--><!-- </set>--><!--</constructor-arg>--><!--<constructor-arg index="6">--><!-- <props>--><!-- <prop key="name">zhangsan</prop>--><!-- </props>--><!--</constructor-arg>--><constructor-arg name="a" value="12"></constructor-arg><constructor-arg name="winktoMapper" ref="winktoMapper"></constructor-arg><constructor-arg name="arrays"><array><value>1</value><ref bean="winktoMapper"></ref></array></constructor-arg><constructor-arg name="list"><list><value>2</value><ref bean="winktoMapper"></ref></list></constructor-arg><constructor-arg name="map"><map><entry key="name" value="zhangsan"></entry><entry key="service" value-ref="winktoMapper"></entry></map></constructor-arg><constructor-arg name="set"><set><value>3</value><ref bean="winktoMapper"></ref></set></constructor-arg><constructor-arg name="properties"><props><prop key="name">zhangsan</prop></props></constructor-arg></bean>
</beans>
测试
@Test
public void test(){ClassPathXmlApplicationContext classPathXmlApplicationContext =new ClassPathXmlApplicationContext("applicationContent.xml");WinktoService winktoService = classPathXmlApplicationContext.getBean("winktoService", WinktoService.class);winktoService.service();
}
5.2.2、set
mapper
public interface WinktoMapper {void mapper();
}
public class WinktoMapperImpl implements WinktoMapper {public void mapper() {System.out.println("假装自己有了数据库查询");}
}
service
public interface WinktoService {void service();
}
public class WinktoServiceImpl implements WinktoService {private int a;private WinktoMapperImpl winktoMapper;private Object[] arrays;private List list;private Map<String,Object> map;private Set set;private Properties properties;public int getA() {return a;}public void setA(int a) {this.a = a;}public WinktoMapperImpl getWinktoMapper() {return winktoMapper;}public void setWinktoMapper(WinktoMapperImpl winktoMapper) {this.winktoMapper = winktoMapper;}public Object[] getArrayList() {return arrays;}public void setArrayList(Object[] arrays) {this.arrays = arrays;}public List getList() {return list;}public void setList(List list) {this.list = list;}public Map<String, Object> getMap() {return map;}public void setMap(Map<String, Object> map) {this.map = map;}public Set getSet() {return set;}public void setSet(Set set) {this.set = set;}public Properties getProperties() {return properties;}public void setProperties(Properties properties) {this.properties = properties;}public void service() {System.out.println("假装自己开启了事务");winktoMapper.mapper();System.out.println("=================");System.out.println(toString());System.out.println("=================");System.out.println("假装自己提交了事务");}@Overridepublic String toString() {return "WinktoServiceImpl{" +"a=" + a +", winktoMapper=" + winktoMapper +", arrays=" + arrays +", list=" + list +", map=" + map +", set=" + set +", properties=" + properties +'}';}
}
aplicationContent.xml
<bean id="winktoMapper" class="club.winkto.mapper.WinktoMapperImpl">
</bean>
<bean id="winktoService" class="club.winkto.service.WinktoServiceImpl"><property name="a" value="12"></property><property name="winktoMapper" ref="winktoMapper"></property><property name="arrayList"><array><value>1</value><ref bean="winktoMapper"></ref></array></property><property name="list"><list><value>2</value><ref bean="winktoMapper"></ref></list></property><property name="map"><map><entry key="name" value="zhangsan"></entry><entry key="service" value-ref="winktoMapper"></entry></map></property><property name="set"><set><value>3</value><ref bean="winktoMapper"></ref></set></property><property name="properties"><props><prop key="name">zhangsan</prop></props></property>
</bean>
测试
@Test
public void test(){ClassPathXmlApplicationContext classPathXmlApplicationContext =new ClassPathXmlApplicationContext("applicationContent.xml");WinktoService winktoService = classPathXmlApplicationContext.getBean("winktoService", WinktoService.class);winktoService.service();
}
5.2.3、p命名空间(简单示范)
导入约束
xmlns:p="http://www.springframework.org/schema/p"
mapper
public interface WinktoMapper {void mapper();
}
public class WinktoMapperImpl implements WinktoMapper {public void mapper() {System.out.println("假装自己有了数据库查询");}
}
service
public interface WinktoService {void service();
}
public class WinktoServiceImpl implements WinktoService {private WinktoMapperImpl winktoMapper;public WinktoMapperImpl getWinktoMapper() {return winktoMapper;}public void setWinktoMapper(WinktoMapperImpl winktoMapper) {this.winktoMapper = winktoMapper;}public void service() {System.out.println("假装自己开启了事务");winktoMapper.mapper();System.out.println("假装自己提交了事务");}
}
aplicationContent.xml
- 对于bean引用用p:xxx-ref
- 否则使用p:xxx
<bean id="winktoMapper" class="club.winkto.mapper.WinktoMapperImpl">
</bean>
<bean id="winktoService" class="club.winkto.service.WinktoServiceImpl" p:winktoMapper-ref="winktoMapper">
</bean>
测试
@Test
public void test(){ClassPathXmlApplicationContext classPathXmlApplicationContext =new ClassPathXmlApplicationContext("applicationContent.xml");WinktoService winktoService = classPathXmlApplicationContext.getBean("winktoService", WinktoService.class);winktoService.service();
}
5.2.4、c命名空间(简单示范)
导入约束
xmlns:c="http://www.springframework.org/schema/c"
mapper
public interface WinktoMapper {void mapper();
}
public class WinktoMapperImpl implements WinktoMapper {public void mapper() {System.out.println("假装自己有了数据库查询");}
}
service
public interface WinktoService {void service();
}
public class WinktoServiceImpl implements WinktoService {private WinktoMapperImpl winktoMapper;public WinktoMapperImpl getWinktoMapper() {return winktoMapper;}public void setWinktoMapper(WinktoMapperImpl winktoMapper) {this.winktoMapper = winktoMapper;}public void service() {System.out.println("假装自己开启了事务");winktoMapper.mapper();System.out.println("假装自己提交了事务");}
}
aplicationContent.xml
- 对于bean引用用c:xxx-ref
- 否则使用c:xxx
- c命名空间可以额外使用c:_0-ref
<bean id="winktoMapper" class="club.winkto.mapper.WinktoMapperImpl">
</bean>
<!--<bean id="winktoService" class="club.winkto.service.WinktoServiceImpl" c:winktoMapper-ref="winktoMapper">-->
<!--</bean>-->
<bean id="winktoService" class="club.winkto.service.WinktoServiceImpl" c:_0-ref="winktoMapper">
</bean>
测试
@Test
public void test(){ClassPathXmlApplicationContext classPathXmlApplicationContext =new ClassPathXmlApplicationContext("applicationContent.xml");WinktoService winktoService = classPathXmlApplicationContext.getBean("winktoService", WinktoService.class);winktoService.service();
}
5.3、配置文件模块化
5.3.1、配置文件并列
ClassPathXmlApplicationContext classPathXmlApplicationContext =new ClassPathXmlApplicationContext("applicationContent.xml","beans.xml");
5.3.2、主从配置文件
在applicationContent.xml导入beans.xml
<import resource="beans.xml"/>
6、Spring注解开发(IOC部分)
Spring是轻代码而重配置的框架,配置比较繁重,影响开发效率,所以注解开发是一种趋势,注解代 替xml配置文件可以简化配置,提高开发效率
开启注解扫描
<!--注解的组件扫描-->
<context:component-scan base-package="club.winkto"></context:component-scan>
6.1、注册bean
注解 | 说明 |
---|---|
@Component | 使用在类上用于实例化Bean |
@Controller | 使用在web层类上用于实例化Bean |
@Service | 使用在service层类上用于实例化Bean |
@Repository | 使用在dao层类上用于实例化Bean |
6.2、依赖注入
注解 | 说明 |
---|---|
@Autowired | 使用在字段上用于根据类型依赖注入(先根据type,类型不唯一根据name自动装配的) |
@Qualifier | 结合@Autowired一起使用,根据名称进行依赖注入 |
@Resource | 相当于@Autowired+@Qualifier,按照名称进行注入(很少用且jdk11在spring核心包不包含此注解) |
@Value | 注入普通属性(也可以注入spring配置文件里的其他普通属性使用${},上面三个注入引用类型) |
6.3、初始销毁
注解 | 说明 |
---|---|
@PostConstruct | 使用在方法上标注该方法是Bean的初始化方法 |
@PreDestroy | 使用在方法上标注该方法是Bean的销毁方法 |
6.4、新注解
注解 | 说明 |
---|---|
@Configuration | 用于指定当前类是一个Spring 配置类,当创建容器时会从该类上加载注解 |
@Bean | 使用在方法上,标注将该方法的返回值存储到 Spring 容器中 |
@PropertySource | 用于加载 properties 文件中的配置 |
@ComponentScan | 用于指定 Spring 在初始化容器时要扫描的包 |
@Import | 用于导入其他配置类 |
6.5、注解快速入门
mapper
public interface WinktoMapper {void mapper();
}
@Repository("winktoMapper")
public class WinktoMapperImpl implements WinktoMapper {public void mapper() {System.out.println("假装自己有了数据库查询");}
}
service
public interface WinktoService {void service();
}
@Service("winktoService")
public class WinktoServiceImpl implements WinktoService {@Value("12")private int a;@Autowiredprivate WinktoMapperImpl winktoMapper;public WinktoServiceImpl() {}public WinktoServiceImpl(int a, WinktoMapperImpl winktoMapper) {this.a = a;this.winktoMapper = winktoMapper;}public int getA() {return a;}public void setA(int a) {this.a = a;}public WinktoMapperImpl getWinktoMapper() {return winktoMapper;}public void setWinktoMapper(WinktoMapperImpl winktoMapper) {this.winktoMapper = winktoMapper;}public void service() {System.out.println("假装自己开启了事务");winktoMapper.mapper();System.out.println("=================");System.out.println(toString());System.out.println("=================");System.out.println("假装自己提交了事务");}@Overridepublic String toString() {return "WinktoServiceImpl{" +"a=" + a +", winktoMapper=" + winktoMapper +'}';}
}
aplicationContent.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:p="http://www.springframework.org/schema/p"xmlns:c="http://www.springframework.org/schema/c"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttps://www.springframework.org/schema/context/spring-context.xsd"><!--注解的组件扫描--><context:component-scan base-package="club.winkto"></context:component-scan>
</beans>
测试
@Test
public void test(){ClassPathXmlApplicationContext classPathXmlApplicationContext =new ClassPathXmlApplicationContext("applicationContent.xml");WinktoService winktoService = classPathXmlApplicationContext.getBean("winktoService", WinktoService.class);winktoService.service();
}
6.6、纯注解开发
导入依赖
<dependencies><!--spring--><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.3.9</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.13</version><scope>test</scope></dependency><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.9.6</version></dependency>
</dependencies>
mapper
public interface WinktoMapper {void mapper();
}
@Repository("winktoMapper")
public class WinktoMapperImpl implements WinktoMapper {@Value("${name}")private String name;public void mapper() {System.out.println(name);System.out.println("假装自己有了数据库查询");}
}
service
public interface WinktoService {void service();
}
@Service("winktoService")
public class WinktoServiceImpl implements WinktoService {@Value("12")private int a;@Autowiredprivate WinktoMapperImpl winktoMapper;public WinktoServiceImpl() {}public WinktoServiceImpl(int a, WinktoMapperImpl winktoMapper) {this.a = a;this.winktoMapper = winktoMapper;}public int getA() {return a;}public void setA(int a) {this.a = a;}public WinktoMapperImpl getWinktoMapper() {return winktoMapper;}public void setWinktoMapper(WinktoMapperImpl winktoMapper) {this.winktoMapper = winktoMapper;}public void service() {System.out.println("假装自己开启了事务");winktoMapper.mapper();System.out.println("=================");System.out.println(toString());System.out.println("=================");System.out.println("假装自己提交了事务");}@Overridepublic String toString() {return "WinktoServiceImpl{" +"a=" + a +", winktoMapper=" + winktoMapper +'}';}
}
config
@Configuration
@PropertySource("classpath:database.properties")
public class WinktoMapperConfig {@Value("${name}")private String name;
}
@Configuration
@ComponentScan("club.winkto")
@Import(WinktoMapperConfig.class)
public class WinktoConfig {@Beanpublic ObjectMapper objectMapper(){return new ObjectMapper();}
}
测试
@Test
public void test() throws JsonProcessingException {AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(WinktoConfig.class);WinktoService winktoService = annotationConfigApplicationContext.getBean("winktoService", WinktoService.class);ObjectMapper objectMapper = annotationConfigApplicationContext.getBean("objectMapper", ObjectMapper.class);System.out.println(objectMapper.writeValueAsString(winktoService));winktoService.service();
}
6.7、Spring整合junit
导入依赖
<dependencies><!--spring--><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.3.9</version></dependency><!--spring-test--><dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>5.3.9</version><scope>test</scope></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.13</version><scope>test</scope></dependency><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.9.6</version></dependency>
</dependencies>
修改Test类
@RunWith(SpringJUnit4ClassRunner.class)
//@ContextConfiguration(value = {"classpath:applicationContext.xml"})
@ContextConfiguration(classes = {WinktoConfig.class})
public class MyTest {@Autowiredprivate WinktoService winktoService;@Autowiredprivate ObjectMapper objectMapper;@Testpublic void test() throws JsonProcessingException {System.out.println(objectMapper.writeValueAsString(winktoService));winktoService.service();}
}
7、动态代理
7.1、JDK动态代理
基于接口的动态代理技术:利用拦截器(必须实现invocationHandler)加上反射机制生成 一个代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理,从而实现方法增强
7.2.1、JDK动态代理实现
service
public interface CRUD {void select();void insert();void update();void delete();
}
@Service
public class CRUDService implements CRUD {public void select() {System.out.println("假装自己执行了select");}public void insert() {System.out.println("假装自己执行了insert");}public void update() {System.out.println("假装自己执行了update");}public void delete() {System.out.println("假装自己执行了delete");}
}
config
@Configuration
@ComponentScan("cn.winkto")
public class WinktoConfig {@Bean("objectMapper")public ObjectMapper getObjectMapper(){return new ObjectMapper();}
}
代理
@Component
public class JDKProxy {public Object proxy(final CRUDService crudService){return Proxy.newProxyInstance(crudService.getClass().getClassLoader(), crudService.getClass().getInterfaces(), new InvocationHandler() {public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("代理前");Object invoke = method.invoke(crudService, args);System.out.println("代理后");return invoke;}});}
}
测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {WinktoConfig.class})
public class MyTest {@Autowiredprivate JDKProxy jdkProxy;@Autowiredprivate CRUDService crudService;@Testpublic void test(){CRUD proxy = (CRUD) jdkProxy.proxy(crudService);proxy.select();}
}
7.2、CGLIB代理
基于父类的动态代理技术:动态生成一个要代理的子类,子类重写要代理的类的所有不是 final的方法。在子类中采用方法拦截技术拦截所有的父类方法的调用,顺势织入横切逻辑,对方法进行 增强
7.2.1、CGLIB代理的实现
service
public interface CRUD {void select();void insert();void update();void delete();
}
@Service
public class CRUDService implements CRUD {public void select() {System.out.println("假装自己执行了select");}public void insert() {System.out.println("假装自己执行了insert");}public void update() {System.out.println("假装自己执行了update");}public void delete() {System.out.println("假装自己执行了delete");}
}
config
@Configuration
@ComponentScan("cn.winkto")
public class WinktoConfig {@Bean("objectMapper")public ObjectMapper getObjectMapper(){return new ObjectMapper();}
}
代理
@Component
public class CglibProxy {public Object proxy(final CRUDService crudService){return Enhancer.create(crudService.getClass(), new MethodInterceptor() {public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {System.out.println("代理前");Object invoke = method.invoke(crudService, objects);System.out.println("代理后");return invoke;}});}
}
测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {WinktoConfig.class})
public class MyTest {@Autowiredprivate JDKProxy jdkProxy;@Autowiredprivate CglibProxy cglibProxy;@Autowiredprivate CRUDService crudService;@Testpublic void test1(){CRUD proxy = (CRUD) cglibProxy.proxy(crudService);proxy.select();}
}
8、Spring AOP
AOP 是 OOP(面向对象编程) 的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率
- 在程序运行期间,在不修改源码的情况下对方法进行功能增强
- 逻辑清晰,开发核心业务的时候,不必关注增强业务的代码
- 减少重复代码,提高开发效率,便于后期维护
8.1、Aop术语解释
- Target(目标对象):代理的目标对象
- Proxy (代理):一个类被 AOP 织入增强后,就产生一个结果代理类
- Joinpoint(连接点):所谓连接点是指那些可以被拦截到的点。在spring中,这些点指的是方法,因为 spring只支持方法类型的连接点
- Pointcut(切入点):所谓切入点是指我们要对哪些 Joinpoint 进行拦截的定义
- Advice(通知/ 增强):所谓通知是指拦截到 Joinpoint 之后所要做的事情就是通知 分类:前置通知、后置通知、异常通知、最终通知、环绕通知
- Aspect(切面):是切入点和通知(引介)的结合
- Weaving(织入):是指把增强应用到目标对象来创建新的代理对象的过程。spring采用动态代理织 入,而AspectJ采用编译期织入和类装载期织入
8.2、Aop快速入门
导入依赖
<dependencies><!--spring--><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.3.9</version></dependency><!--spring-test--><dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>5.3.9</version><scope>test</scope></dependency><!--aop--><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.6</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.13</version><scope>test</scope></dependency><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.9.6</version></dependency>
</dependencies>
service
public interface CRUD {void select();void insert();void update();void delete();
}
public class CRUDService implements CRUD {public void select() {System.out.println("假装自己执行了select");}public void insert() {System.out.println("假装自己执行了insert");}public void update() {System.out.println("假装自己执行了update");}public void delete() {System.out.println("假装自己执行了delete");}
}
aop
public class CRUDAdvice {public void before(){System.out.println("前置通知");}public void afterReturning(){System.out.println("后置通知");}public void afterThrowing(){System.out.println("异常通知");}public void after(){System.out.println("最终通知");}public void around(ProceedingJoinPoint ProceedingJoinPoint){System.out.println("环绕开始");try {ProceedingJoinPoint.proceed();} catch (Throwable throwable) {throwable.printStackTrace();}System.out.println("环绕结束");}
}
applicationContent.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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/aophttps://www.springframework.org/schema/aop/spring-aop.xsd"><bean id="crudService" class="cn.winkto.service.CRUDService"></bean><bean id="crudAdvice" class="cn.winkto.aop.CRUDAdvice"></bean><aop:config><aop:aspect ref="crudAdvice"><aop:before method="before" pointcut="execution(* cn.winkto.service.CRUDService.*(..))"></aop:before><aop:after method="afterReturning" pointcut="execution(* cn.winkto.service.CRUDService.*(..))"></aop:after></aop:aspect></aop:config>
</beans>
测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(value = {"classpath:applicationContent.xml"})
public class MyTest {@Autowiredprivate CRUD crud;@Testpublic void test(){crud.select();}
}
8.3、Aop详解
8.3.1、切点表达式
execution([修饰符] 返回值类型 包名.类名.方法名(参数))
- 访问修饰符可以省略
- 返回值类型、包名、类名、方法名可以使用星号 * 代替,代表任意
- 包名与类名之间一个点 . 代表当前包下的类,两个点 … 表示当前包及其子包下的类
- 参数列表可以使用两个点 … 表示任意个数,任意类型的参数列表
8.3.2、切点表达式的抽取
<?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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/aophttps://www.springframework.org/schema/aop/spring-aop.xsd"><bean id="crudService" class="cn.winkto.service.CRUDService"></bean><bean id="crudAdvice" class="cn.winkto.aop.CRUDAdvice"></bean><aop:config><aop:aspect ref="crudAdvice"><aop:pointcut id="point1" expression="execution(* cn.winkto.service.CRUDService.*(..))"/><aop:before method="before" pointcut-ref="point1"></aop:before><aop:after method="afterReturning" pointcut-ref="point1"></aop:after></aop:aspect></aop:config>
</beans>
<?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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/aophttps://www.springframework.org/schema/aop/spring-aop.xsd"><bean id="crudService" class="cn.winkto.service.CRUDService"></bean><bean id="crudAdvice" class="cn.winkto.aop.CRUDAdvice"></bean><aop:config><aop:pointcut id="point1" expression="execution(* cn.winkto.service.CRUDService.*(..))"/><aop:aspect ref="crudAdvice"><aop:before method="before" pointcut-ref="point1"></aop:before><aop:after method="afterReturning" pointcut-ref="point1"></aop:after></aop:aspect></aop:config>
</beans>
8.3.3、通知类型
<aop:通知类型 method=“通知类中方法名” pointcut=“切点表达式"></aop:通知类型>
名称 | 标签 | 说明 |
---|---|---|
前置通知 | <aop:before> | 用于配置前置通知,指定增强的方法在切入点方法之前执行 |
后置通知 | <aop:afterReturning> | 用于配置后置通知。指定增强的方法在切入点方法之后执行 |
异常通知 | <aop:afterThrowing> | 用于配置异常通知。指定增强的方法出现异常后执行 |
最终通知 | <aop:after> | 用于配置最终通知。无论切入点方法执行时是否有异常,都会执行 |
环绕通知 | <aop:around> | 用于配置环绕通知。开发者可以手动控制增强代码在什么时候执行 |
后置通知与异常通知互斥,环绕通知一般独立使用
9、Spring注解开发(AOP部分)
导入依赖
<dependencies><!--spring--><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.3.9</version></dependency><!--spring-test--><dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>5.3.9</version><scope>test</scope></dependency><!--aop--><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.6</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.13</version><scope>test</scope></dependency><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.9.6</version></dependency>
</dependencies>
service
public interface CRUD {void select();void insert();void update();void delete();
}
public class CRUDService implements CRUD {public void select() {System.out.println("假装自己执行了select");}public void insert() {System.out.println("假装自己执行了insert");}public void update() {System.out.println("假装自己执行了update");}public void delete() {System.out.println("假装自己执行了delete");}
}
aop
@Component
//标注为切面类
@Aspect
public class CRUDAdvice {@Pointcut("execution(* cn.winkto.service.CRUDService.*(..))")public void point(){}@Before("point()")public void before(){System.out.println("前置通知");}@AfterReturning("point()")public void afterReturning(){System.out.println("后置通知");}public void afterThrowing(){System.out.println("异常通知");}public void after(){System.out.println("最终通知");}public void around(ProceedingJoinPoint ProceedingJoinPoint){System.out.println("环绕开始");try {ProceedingJoinPoint.proceed();} catch (Throwable throwable) {throwable.printStackTrace();}System.out.println("环绕结束");}
}
config
@Configuration
@ComponentScan("cn.winkto")
//开启自动aop代理
@EnableAspectJAutoProxy
public class WinktoConfig {@Bean("objectMapper")public ObjectMapper getObjectMapper(){return new ObjectMapper();}
}
测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {WinktoConfig.class})
public class MyTest {@Autowiredprivate CRUD crud;@Testpublic void test(){crud.select();}
}
10、Spring事务
- 编程式事务:直接把事务的代码和业务代码耦合到一起,在实际开发中不用(下文不进行详细阐述)
- 声明式事务:采用配置的方式来实现的事务控制,业务代码与事务代码实现解耦合
10.1、声明式事务(基于XML,整合mybatis)
实体类
public class Person {private int pid;private String pname;private String ppassword;
}
mapper
public interface PersonMapper {List<Person> selectPerson();
}
public class PersonMapperImpl implements PersonMapper {private SqlSession sqlSession;public PersonMapperImpl(SqlSession sqlSession) {this.sqlSession = sqlSession;}public List<Person> selectPerson() {return sqlSession.getMapper(PersonMapper.class).selectPerson();}
}
映射文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.winkto.mapper.PersonMapper"><select id="selectPerson" resultType="Person">select * from person</select>
</mapper>
数据库配置文件
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone =Asia/Shanghai
user=root
password=blingbling123.
mybatis配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd"><configuration><settings><setting name="logImpl" value="STDOUT_LOGGING"/></settings><typeAliases><package name="cn.winkto.bean"></package></typeAliases><mappers><package name="cn.winkto.mapper"/></mappers>
</configuration>
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" xmlns:tx="http://www.springframework.org/schema/tx"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttps://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/txhttp://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"><!--开启注解扫描--><context:component-scan base-package="cn.winkto" /><context:property-placeholder location="classpath:database.properties"/><bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"><property name="driverClassName" value="${driver}"/><property name="url" value="${url}"/><property name="username" value="${user}"/><property name="password" value="${password}"/></bean><!--sqlSessionFactory--><bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"><property name="dataSource" ref="dataSource" /><property name="configLocation" value="mybatis-config.xml" /></bean><!--sqlsession--><bean id="sqlsession" class="org.mybatis.spring.SqlSessionTemplate"><constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory" /></bean><bean id="personMapper" class="cn.winkto.mapper.PersonMapperImpl"><constructor-arg name="sqlSession" ref="sqlsession" /></bean><!--事务管理器--><bean id="tx" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><constructor-arg name="dataSource" ref="dataSource" /></bean><!--通知增强--><tx:advice id="txAdvice" transaction-manager="tx"><tx:attributes><tx:method name="*"/></tx:attributes></tx:advice><!--织入事务--><aop:config><aop:pointcut id="point" expression="execution(* cn.winkto.mapper.PersonMapperImpl.*(..))" /><aop:advisor advice-ref="txAdvice" pointcut-ref="point" /></aop:config>
</beans>
测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(value = {"classpath:applicationContent.xml"})
public class MyTest {@Autowiredprivate PersonMapper personMapper;@Testpublic void test(){System.out.println(personMapper.selectPerson());}
}
10.1.1、 事务参数的配置详解
<tx:method name="selectPerson" isolation="REPEATABLE_READ" propagation="REQUIRED" timeout="-1" read-only="false"/>
- name:切点方法名称(可以使用*做匹配,类似于模糊查询)
- isolation:事务的隔离级别
- propogation:事务的传播行为
- timeout:超时时间
- read-only:是否只读
10.2、声明式事务(基于注解,整合mybatis)
实体类
public class Person {private int pid;private String pname;private String ppassword;
}
数据库配置文件
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone =Asia/Shanghai
user=root
password=blingbling123.
mapper
public interface PersonMapper {List<Person> selectPerson();
}
@Repository("personMapper")
public class PersonMapperImpl implements PersonMapper {@Autowiredprivate SqlSessionTemplate sqlSession;public PersonMapperImpl(SqlSessionTemplate sqlSession) {this.sqlSession = sqlSession;}//配置事务属性(可放置在类上,k)@Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.DEFAULT,timeout = -1,readOnly = true)public List<Person> selectPerson() {return sqlSession.getMapper(PersonMapper.class).selectPerson();}
}
映射文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.winkto.mapper.PersonMapper"><select id="selectPerson" resultType="Person">select * from person</select>
</mapper>
mybatis配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd"><configuration><settings><setting name="logImpl" value="STDOUT_LOGGING"/></settings><typeAliases><package name="cn.winkto.bean"></package></typeAliases><mappers><package name="cn.winkto.mapper"/></mappers>
</configuration>
config
@Configuration
@EnableAspectJAutoProxy
@EnableTransactionManagement
@ComponentScan("cn.winkto")
@PropertySource("classpath:database.properties")public class WinktoConfig {@Value("${driver}")private String driver;@Value("${url}")private String url;@Value("${user}")private String user;@Value("${password}")private String password;@Beanpublic DataSource dataSource(){DruidDataSource druidDataSource = new DruidDataSource();druidDataSource.setDriverClassName(driver);druidDataSource.setUrl(url);druidDataSource.setUsername(user);druidDataSource.setPassword(password);return druidDataSource;}@Beanpublic SqlSessionFactoryBean sqlSessionFactoryBean(@Autowired DataSource dataSource){SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();sqlSessionFactoryBean.setDataSource(dataSource);sqlSessionFactoryBean.setConfigLocation(new ClassPathResource("mybatis-config.xml"));return sqlSessionFactoryBean;}@Bean("sqlSession")public SqlSessionTemplate sqlSessionTemplate(@Autowired SqlSessionFactoryBean sqlSessionFactoryBean) throws Exception {return new SqlSessionTemplate(sqlSessionFactoryBean.getObject());}@Beanpublic DataSourceTransactionManager dataSourceTransactionManager(){return new DataSourceTransactionManager(dataSource());}
}
测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {WinktoConfig.class})
public class MyTest {@Autowiredprivate PersonMapper personMapper;@Testpublic void test(){System.out.println(personMapper.selectPerson());}
}