1、传统开发转为注解驱动开发
传统开发转为注解驱动开发在实际项目开发过程中,最明显的就是体现在ssm项目转变为springboot项目,原本各式各样的配置文件等,全部被取消了,从而采用零配置文件的方式进行开发。也就是使用java代码的形式进行配置,以下全文分析怎么从传统开发转为注解驱动开发,也就是说,怎么将配置文件一步步转变为注解的形式进行开发
(1)spring传统开发
spring中,最常见的传统开发为ssm(spring + springmvc + mybatis),通常情况下,我们需要在项目目录的的src\main\resources
目录中创建applicationContext.xml
配置文件,然后在src\main\webapp\WEB-INF\web.xml
配置扫描我们的自定义文件,告诉DispatcherServlet
加载的文件位置
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<!-- Spring mvc -->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:application-context.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
</web-app>
applicationContext.xml
配置文件是ssm中最核心的配置文件,这里我举例说明通常情况下配置了些什么,后面再分析怎么将传统的配置文件转为注解驱动开发
<?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:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:task="http://www.springframework.org/schema/task"
xmlns:websocket="http://www.springframework.org/schema/websocket"
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/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task.xsd
http://www.springframework.org/schema/websocket
http://www.springframework.org/schema/websocket/spring-websocket.xsd">
<!-- 自动扫描bean,把所有注解类转换成bean -->
<context:component-scan base-package="cn.tellsea"/>
<!-- 加载驱动及Session作用域 -->
<mvc:annotation-driven>
<!--不使用默认消息转换器 -->
<mvc:message-converters register-defaults="false">
<!--spring消息转换器 -->
<bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter"/>
<bean class="org.springframework.http.converter.BufferedImageHttpMessageConverter"/>
<!--解决@Responcebody中文乱码问题 -->
<!-- 将StringHttpMessageConverter的默认编码设为UTF-8 -->
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<constructor-arg value="UTF-8"/>
</bean>
<!--配合fastjson支持 -->
<bean class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">
<property name="defaultCharset" value="UTF-8"/>
<property name="supportedMediaTypes">
<list>
<!--顺序保持这样,避免IE下载出错 -->
<value>text/html;charset=UTF-8</value>
<value>application/json</value>
</list>
</property>
<property name="fastJsonConfig" ref="fastJsonConfig"/>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
<!--配置这里只需要编码-->
<bean id="fastJsonConfig" class="com.alibaba.fastjson.support.config.FastJsonConfig">
<!--默认编码格式 -->
<property name="charset" value="UTF-8"/>
<property name="serializerFeatures">
<list>
<value>WriteNullListAsEmpty</value>
<value>WriteDateUseDateFormat</value>
<value>PrettyFormat</value>
<value>WriteMapNullValue</value>
<value>WriteNullStringAsEmpty</value>
<value>WriteNullListAsEmpty</value>
<value>DisableCircularReferenceDetect</value>
</list>
</property>
</bean>
<!-- 视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/web/"/>
<property name="suffix" value=".jsp"/>
<property name="contentType" value="text/html;charset=UTF-8"/>
</bean>
<!-- 数据库连接池 -->
<!-- 查看监控 http://localhost:8080/spring4.x/druid/index.html -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
init-method="init" destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<!-- 基本属性 url、user、password -->
<property name="url" value="jdbc:mysql://localhost:3306/spring-tellsea?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&useSSL=false"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
<!-- 配置初始化大小、最小、最大 -->
<property name="initialSize" value="30"/>
<property name="minIdle" value="50"/>
<property name="maxActive" value="300"/>
<!-- 配置获取连接等待超时的时间 -->
<property name="maxWait" value="60000"/>
<!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
<property name="timeBetweenEvictionRunsMillis" value="60000"/>
<!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
<property name="minEvictableIdleTimeMillis" value="300000"/>
<property name="validationQuery" value="SELECT 'x'"/>
<property name="testWhileIdle" value="true"/>
<property name="testOnBorrow" value="false"/>
<property name="testOnReturn" value="false"/>
<!-- 打开PSCache,并且指定每个连接上PSCache的大小 -->
<property name="poolPreparedStatements" value="true"/>
<property name="maxPoolPreparedStatementPerConnectionSize" value="20"/>
<!-- 配置监控统计拦截的filters,去掉后监控界面sql无法统计 -->
<property name="filters" value="stat"/>
<!-- 控制sql注入 -->
<property name="proxyFilters">
<list>
<ref bean="wall-filter"/>
</list>
</property>
</bean>
<!-- 给 jdbcTemplate 注入 dataSource-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 防止sql注入 -->
<bean id="wall-filter-config" class="com.alibaba.druid.wall.WallConfig" init-method="init">
<!-- 指定配置装载的目录 -->
<property name="dir" value="META-INF/druid/wall/mysql"/>
</bean>
<bean id="wall-filter" class="com.alibaba.druid.wall.WallFilter">
<property name="dbType" value="mysql"/>
<property name="config" ref="wall-filter-config"/>
</bean>
<!-- 事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 声明式容器事务管理 ,transaction-manager指定事务管理器为transactionManager -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="save*" propagation="REQUIRED"/>
<tx:method name="add*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRED"/>
<tx:method name="delete*" propagation="REQUIRED"/>
<tx:method name="lock*" propagation="REQUIRED"/>
<tx:method name="enable*" propagation="REQUIRED"/>
<tx:method name="exit*" propagation="REQUIRED"/>
<tx:method name="*" read-only="true"/>
</tx:attributes>
</tx:advice>
<!--数据逻辑事物管理-->
<aop:config expose-proxy="true">
<!-- 只对业务逻辑层实施事务 -->
<aop:pointcut id="txPointcut" expression="execution(* cn.tellsea.service..*.*(..))"/>
<!-- Advisor定义,切入点和通知分别为txPointcut、txAdvice -->
<aop:advisor pointcut-ref="txPointcut" advice-ref="txAdvice"/>
</aop:config>
<!-- 文件上传 -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="defaultEncoding" value="UTF-8"></property>
<property name="resolveLazily" value="true"></property>
<property name="maxInMemorySize" value="40960"></property>
<property name="maxUploadSizePerFile" value="52428800"></property>
</bean>
<!-- Bean解析器,级别高于默认解析器,寻找bean对象进行二次处理 -->
<bean id="beanNameViewResolver"
class="org.springframework.web.servlet.view.BeanNameViewResolver" p:order="0">
</bean>
<!-- 拦截器配置 -->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="cn.tellsea.interceptor.MyInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
<!-- 获取Spring Context -->
<bean class="cn.tellsea.util.SpringContextUtils" id="SpringContextUtils"/>
<!-- 异常处理 -->
<bean id="exceptionResolver" class="cn.tellsea.handle.GlobalExceptionHandle"/>
<!-- 允许swagger界面-->
<mvc:resources location="classpath:/META-INF/resources/" mapping="swagger-ui.html"/>
<mvc:resources location="classpath:/META-INF/resources/webjars/" mapping="/webjars/**"/>
</beans>
(2)spring注解驱动开发
spring注解驱动开发的典型代表springboot,全部采用注解的形式进各种配置信息的注入等操作
注解驱动开发就是使用注解的方式,达到扫描、注入、定义等操作,以下两种方式等效
<?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 id="person" class="com.ldc.bean.Person">
<property name="age" value="18"></property>
<property name="name" value="张三"></property>
</bean>
</beans>
/**
* @Description 配置类就等同以前的配置文件
*/
@Configuration //告诉Spring这是一个配置类
public class MainConfig {
//相当于xml配置文件中的<bean>标签,告诉容器注册一个bean
//之前xml文件中<bean>标签有bean的class类型,那么现在注解方式的类型当然也就是返回值的类型
//之前xml文件中<bean>标签有bean的id,现在注解的方式默认用的是方法名来作为bean的id
@Bean
public Person person() {
return new Person("lisi",20);
}
}
(3)Spring注解驱动开发核心知识图
2、传统开发转为注解驱动开发(配置文件分析)
下面,我们通过案例,来体现怎么将传统开发转为注解驱动开发, 案例的核心分析为实际项目中最常见的相关xml配置信息
(1)案例:spring包扫描配置
传统开发
<!-- 自动扫描bean,把所有注解类转换成bean -->
<context:component-scan base-package="cn.tellsea"/>
注解驱动开发
import org.springframework.context.annotation.ComponentScan;
/**
* @author Tellsea
* @date 2020-8-13
*/
@ComponentScan("cn.tellsea")
public class ComponentScanConfig {
}
(2)案例:定制http消息转换器为FastJson
传统开发
<!-- 加载驱动及Session作用域 -->
<mvc:annotation-driven>
<!--不使用默认消息转换器 -->
<mvc:message-converters register-defaults="false">
<!--spring消息转换器 -->
<bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter"/>
<bean class="org.springframework.http.converter.BufferedImageHttpMessageConverter"/>
<!--解决@Responcebody中文乱码问题 -->
<!-- 将StringHttpMessageConverter的默认编码设为UTF-8 -->
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<constructor-arg value="UTF-8"/>
</bean>
<!--配合fastjson支持 -->
<bean class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">
<property name="defaultCharset" value="UTF-8"/>
<property name="supportedMediaTypes">
<list>
<!--顺序保持这样,避免IE下载出错 -->
<value>text/html;charset=UTF-8</value>
<value>application/json</value>
</list>
</property>
<property name="fastJsonConfig" ref="fastJsonConfig"/>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
<!--配置这里只需要编码-->
<bean id="fastJsonConfig" class="com.alibaba.fastjson.support.config.FastJsonConfig">
<!--默认编码格式 -->
<property name="charset" value="UTF-8"/>
<property name="serializerFeatures">
<list>
<value>WriteNullListAsEmpty</value>
<value>WriteDateUseDateFormat</value>
<value>PrettyFormat</value>
<value>WriteMapNullValue</value>
<value>WriteNullStringAsEmpty</value>
<value>WriteNullListAsEmpty</value>
<value>DisableCircularReferenceDetect</value>
</list>
</property>
</bean>
注解驱动开发
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.support.config.FastJsonConfig;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import java.util.ArrayList;
import java.util.List;
/**
* Http FastJSON 转换器
*
* @author Tellsea
* @date 2019/8/13
*/
@Configuration
public class HttpConverterConfig {
@Bean
public HttpMessageConverters fastJsonHttpMessageConverters() {
// 1.定义一个converters转换消息的对象
FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();
// 2.添加fastjson的配置信息,比如: 是否需要格式化返回的json数据
FastJsonConfig fastJsonConfig = new FastJsonConfig();
fastJsonConfig.setSerializerFeatures(
SerializerFeature.WriteNullListAsEmpty,
SerializerFeature.WriteDateUseDateFormat,
SerializerFeature.PrettyFormat,
SerializerFeature.WriteMapNullValue,
SerializerFeature.WriteNullStringAsEmpty,
SerializerFeature.WriteNullListAsEmpty,
SerializerFeature.DisableCircularReferenceDetect
);
// 3.在converter中添加配置信息
fastConverter.setFastJsonConfig(fastJsonConfig);
// 4.处理中文乱码问题
List<MediaType> fastMediaTypes = new ArrayList<>();
fastMediaTypes.add(MediaType.APPLICATION_JSON_UTF8);
fastConverter.setSupportedMediaTypes(fastMediaTypes);
// 5.将converter赋值给HttpMessageConverter
HttpMessageConverter<?> converter = fastConverter;
// 6.返回HttpMessageConverters对象
return new HttpMessageConverters(converter);
}
}
类中使用了springboot的消息转换器HttpMessageConverters
,当然也可以使用spring原生的方式改写,我这里以实际使用的案例为准
(3)案例:配置视图解析器
传统开发
<!-- 视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/web/"/>
<property name="suffix" value=".jsp"/>
<property name="contentType" value="text/html;charset=UTF-8"/>
</bean>
注解驱动开发
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.servlet.view.JstlView;
/**
* @author Tellsea
* @date 2020-8-13
*/
@Configuration
public class SpringMvcConfig {
@Bean
public InternalResourceViewResolver internalResourceViewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setViewClass(JstlView.class);
resolver.setPrefix("/web/");
resolver.setSuffix(".jsp");
resolver.setContentType("text/html;charset=UTF-8");
return resolver;
}
}
在springboot中,配置视图解析器可以直接通过application.yml
文件进行配置
(4)案例:配置防止sql注入
传统开发
<!-- 防止sql注入 -->
<bean id="wall-filter-config" class="com.alibaba.druid.wall.WallConfig" init-method="init">
<!-- 指定配置装载的目录 -->
<property name="dir" value="META-INF/druid/wall/mysql"/>
</bean>
<bean id="wall-filter" class="com.alibaba.druid.wall.WallFilter">
<property name="dbType" value="mysql"/>
<property name="config" ref="wall-filter-config"/>
</bean>
注解驱动开发
import com.alibaba.druid.wall.WallConfig;
import com.alibaba.druid.wall.WallFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author Tellsea
* @date 2020-8-13
*/
@Configuration
public class DruidDataSourceConfig {
@Bean("wall-filter-config")
public WallConfig wallConfig() {
WallConfig wallConfig = new WallConfig();
wallConfig.setDir("META-INF/druid/wall/mysql");
return wallConfig;
}
@Bean("wall-filter")
public WallFilter wallFilter() {
WallFilter wallFilter = new WallFilter();
wallFilter.setDbType("mysql");
wallFilter.setConfig(wallConfig());
return wallFilter;
}
}
(5)案例:配置数据库连接池
传统开发
<!-- 数据库连接池 -->
<!-- 查看监控 http://localhost:8080/spring4.x/druid/index.html -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
init-method="init" destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<!-- 基本属性 url、user、password -->
<property name="url" value="jdbc:mysql://localhost:3306/spring-tellsea?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&useSSL=false"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
<!-- 配置初始化大小、最小、最大 -->
<property name="initialSize" value="30"/>
<property name="minIdle" value="50"/>
<property name="maxActive" value="300"/>
<!-- 配置获取连接等待超时的时间 -->
<property name="maxWait" value="60000"/>
<!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
<property name="timeBetweenEvictionRunsMillis" value="60000"/>
<!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
<property name="minEvictableIdleTimeMillis" value="300000"/>
<property name="validationQuery" value="SELECT 'x'"/>
<property name="testWhileIdle" value="true"/>
<property name="testOnBorrow" value="false"/>
<property name="testOnReturn" value="false"/>
<!-- 打开PSCache,并且指定每个连接上PSCache的大小 -->
<property name="poolPreparedStatements" value="true"/>
<property name="maxPoolPreparedStatementPerConnectionSize" value="20"/>
<!-- 配置监控统计拦截的filters,去掉后监控界面sql无法统计 -->
<property name="filters" value="stat"/>
<!-- 控制sql注入 -->
<property name="proxyFilters">
<list>
<ref bean="wall-filter"/>
</list>
</property>
</bean>
注解驱动开发
import com.alibaba.druid.filter.Filter;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.wall.WallConfig;
import com.alibaba.druid.wall.WallFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
/**
* @author Tellsea
* @date 2020-8-13
*/
@Configuration
public class DruidDataSourceConfig {
@Bean("wall-filter-config")
public WallConfig wallConfig() {
WallConfig wallConfig = new WallConfig();
wallConfig.setDir("META-INF/druid/wall/mysql");
return wallConfig;
}
@Bean("wall-filter")
public WallFilter wallFilter() {
WallFilter wallFilter = new WallFilter();
wallFilter.setDbType("mysql");
wallFilter.setConfig(wallConfig());
return wallFilter;
}
@Bean("dataSource")
public DataSource dataSource() throws SQLException {
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setDriverClassName("com.mysql.jdbc.Driver");
druidDataSource.setUrl("jdbc:mysql://localhost:3306/zhengwu?useSSL=false");
druidDataSource.setUsername("root");
druidDataSource.setPassword("123456");
druidDataSource.setInitialSize(30);
druidDataSource.setMinIdle(50);
druidDataSource.setMaxActive(300);
druidDataSource.setMaxWait(60000);
druidDataSource.setTimeBetweenEvictionRunsMillis(60000);
druidDataSource.setMinEvictableIdleTimeMillis(300000);
druidDataSource.setValidationQuery("SELECT 'x'");
druidDataSource.setTestWhileIdle(true);
druidDataSource.setTestOnBorrow(false);
druidDataSource.setTestOnReturn(false);
druidDataSource.setPoolPreparedStatements(true);
druidDataSource.setMaxPoolPreparedStatementPerConnectionSize(20);
druidDataSource.setFilters("stat");
List<Filter> filters = new ArrayList<>();
filters.add(wallFilter());
druidDataSource.setProxyFilters(filters);
return druidDataSource;
}
}
(6)案例:配置JdbcTemplate注入数据源
传统开发
<!-- 给 jdbcTemplate 注入 dataSource-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
注解驱动开发
@Bean
public JdbcTemplate jdbcTemplate() throws SQLException {
JdbcTemplate jdbcTemplate = new JdbcTemplate();
jdbcTemplate.setDataSource(dataSource());
return jdbcTemplate;
}
在配置数据库连接池的配置类中,增加JdbcTemplate的Bean配置
(7)案例:配置事务管理器
传统开发
<!-- 事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 声明式容器事务管理 ,transaction-manager指定事务管理器为transactionManager -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="save*" propagation="REQUIRED"/>
<tx:method name="add*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRED"/>
<tx:method name="delete*" propagation="REQUIRED"/>
<tx:method name="lock*" propagation="REQUIRED"/>
<tx:method name="enable*" propagation="REQUIRED"/>
<tx:method name="exit*" propagation="REQUIRED"/>
<tx:method name="*" read-only="true"/>
</tx:attributes>
</tx:advice>
<!--数据逻辑事物管理-->
<aop:config expose-proxy="true">
<!-- 只对业务逻辑层实施事务 -->
<aop:pointcut id="txPointcut" expression="execution(* cn.tellsea.service..*.*(..))"/>
<!-- Advisor定义,切入点和通知分别为txPointcut、txAdvice -->
<aop:advisor pointcut-ref="txPointcut" advice-ref="txAdvice"/>
</aop:config>
注解驱动开发
import org.aspectj.lang.annotation.Aspect;
import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.interceptor.*;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
/**
* 全局事物配置
* <p>
* REQUIRED :如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
* SUPPORTS :如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
* MANDATORY :如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
* REQUIRES_NEW :创建一个新的事务,如果当前存在事务,则把当前事务挂起。
* NOT_SUPPORTED :以非事务方式运行,如果当前存在事务,则把当前事务挂起。
* NEVER :以非事务方式运行,如果当前存在事务,则抛出异常。
* NESTED :如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于 REQUIRED 。
* 指定方法:通过使用 propagation 属性设置,例如:@Transactional(propagation = Propagation.REQUIRED)
*
* @author Tellsea
* @date 2020/3/7
*/
@Aspect
@Configuration
public class TransactionAdviceConfig {
/**
* 配置方法过期时间,默认-1,永不超时
*/
private final static int TX_METHOD_TIME_OUT = 10;
/**
* 配置切入点表达式,这里解释一下表达式的含义
* 1.execution(): 表达式主体
* 2.第一个*号:表示返回类型,*号表示所有的类型
* 3.com.schcilin.goods.service表示切入点的包名
* 4.第二个*号:表示实现包
* 5.*(..)*号表示所有方法名,..表示所有类型的参数
*/
private static final String POITCUT_EXPRESSION = "execution(* cn.tellsea.service..*.*(..))";
@Autowired
private PlatformTransactionManager platformTransactionManager;
@Bean
public TransactionInterceptor txadvice() {
/* 配置事务管理规则,声明具备管理事务方法名.这里使用public void addTransactionalMethod(String methodName, TransactionAttribute attr)*/
NameMatchTransactionAttributeSource source = new NameMatchTransactionAttributeSource();
Map<String, TransactionAttribute> nameMap = new HashMap<>(16);
/*只读事物、不做更新删除等*/
/*事务管理规则*/
RuleBasedTransactionAttribute readOnlyRule = new RuleBasedTransactionAttribute();
/*设置当前事务是否为只读事务,true为只读*/
readOnlyRule.setReadOnly(true);
/* transactiondefinition 定义事务的隔离级别;
* PROPAGATION_REQUIRED 如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中*/
readOnlyRule.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
RuleBasedTransactionAttribute requireRule = new RuleBasedTransactionAttribute();
/*抛出异常后执行切点回滚*/
requireRule.setRollbackRules(Collections.singletonList(new RollbackRuleAttribute(Exception.class)));
/*PROPAGATION_REQUIRED:事务隔离性为1,若当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。这是默认值。 */
requireRule.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
/*设置事务失效时间,超过10秒,可根据hytrix,则回滚事务*/
requireRule.setTimeout(TX_METHOD_TIME_OUT);
nameMap.put("add*", requireRule);
nameMap.put("save*", requireRule);
nameMap.put("create*", requireRule);
nameMap.put("insert*", requireRule);
nameMap.put("update*", requireRule);
nameMap.put("delete*", requireRule);
nameMap.put("remove*", requireRule);
/*进行批量操作时*/
nameMap.put("batch*", requireRule);
nameMap.put("get*", readOnlyRule);
nameMap.put("query*", readOnlyRule);
nameMap.put("find*", readOnlyRule);
nameMap.put("select*", readOnlyRule);
nameMap.put("count*", readOnlyRule);
source.setNameMap(nameMap);
return new TransactionInterceptor(platformTransactionManager, source);
}
/**
* 设置切面=切点pointcut+通知TxAdvice
*
* @return
*/
@Bean
public Advisor txAdviceAdvisorByBusiness() {
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression(POITCUT_EXPRESSION);
return new DefaultPointcutAdvisor(pointcut, txadvice());
}
}
(8)案例:注入CommonsMultipartResolver处理文件上传
传统开发
<!-- 文件上传 -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="defaultEncoding" value="UTF-8"></property>
<property name="resolveLazily" value="true"></property>
<property name="maxInMemorySize" value="40960"></property>
<property name="maxUploadSizePerFile" value="52428800"></property>
</bean>
注解驱动开发
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.multipart.MultipartResolver;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;
/**
* @author Tellsea
* @date 2020-8-13
*/
@Configuration
public class MultipartResolverConfig {
@Bean(name = "multipartResolver")
public MultipartResolver multipartResolver() {
CommonsMultipartResolver resolver = new CommonsMultipartResolver();
resolver.setDefaultEncoding("UTF-8");
//resolveLazily属性启用是为了推迟文件解析,以在在UploadAction中捕获文件大小异常
resolver.setResolveLazily(true);
resolver.setMaxInMemorySize(40960);
//上传文件大小 50M 50*1024*1024 bytes
resolver.setMaxUploadSize(50 * 1024 * 1024);
return resolver;
}
}
springmvc中将MultipartFile转为file,需要注入CommonsMultipartResolver,否则报错
java.lang.ClassCastException: org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile cannot be cast to org.springframework.web.multipart.commons.CommonsMultipartFile
springboot中的相关配置为
spring:
servlet:
multipart:
max-file-size: 20MB
max-request-size: 100MB
enabled: true
resolve-lazily: false
(9)案例:配置Bean解析器的处理级别
传统开发
<!-- Bean解析器,级别高于默认解析器,寻找bean对象进行二次处理 -->
<bean id="beanNameViewResolver"
class="org.springframework.web.servlet.view.BeanNameViewResolver" p:order="0">
</bean>
注解驱动开发
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.web.servlet.view.BeanNameViewResolver;
/**
* @author Tellsea
* @date 2020-8-13
*/
@Configuration
public class BeanNameViewResolverConfig {
@Bean
@Order(0)
public BeanNameViewResolver beanNameViewResolver() {
return new BeanNameViewResolver();
}
}
(10)案例:配置自定义拦截器
传统开发
<!-- 拦截器配置 -->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="cn.tellsea.interceptor.MyInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
注解驱动开发
首先编写自定义拦截器,然后将拦截器注册到SpringMVC中
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author Tellsea
* @date 2020-8-13
*/
public class MyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("在业务处理器处理请求之前被调用...");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("在业务处理器处理完请求后被调用...");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("在DispatcherServlet完全处理完请求后被调用...");
}
}
注册自定义拦截器
import cn.tellsea.interceptor.MyInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* @author Tellsea
* @date 2020-8-13
*/
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**");
}
}
(11)案例:配置获取ApplicationContext工具类
传统开发
<!-- 获取Spring Context -->
<bean class="cn.tellsea.util.SpringContextUtils" id="SpringContextUtils"/>
注解驱动开发
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.util.Objects;
/**
* @author Tellsea
* @date 2020-8-13
*/
@Component
public class SpringContextUtils implements ApplicationContextAware {
private static ApplicationContext applicationContext;
public static HttpServletRequest getHttpServletRequest() {
return ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
if (SpringContextUtils.applicationContext == null) {
SpringContextUtils.applicationContext = applicationContext;
}
System.out.println("SpringUtils: {" + applicationContext + "}");
}
/**
* 获取applicationContext
*
* @return
*/
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
/**
* 通过name获取 Bean
*
* @param name
* @return
*/
public static Object getBean(String name) {
return getApplicationContext().getBean(name);
}
/**
* 通过class获取 Bean
*
* @param clazz
* @param <T>
* @return
*/
public static <T> T getBean(Class<T> clazz) {
return getApplicationContext().getBean(clazz);
}
/**
* 通过name,以及Clazz返回指定的Bean
*
* @param name
* @param clazz
* @param <T>
* @return
*/
public static <T> T getBean(String name, Class<T> clazz) {
return getApplicationContext().getBean(name, clazz);
}
}
(12)案例:配置全局异常处理类
传统开发
<!-- 异常处理 -->
<bean id="exceptionResolver" class="cn.tellsea.handle.GlobalExceptionHandle"/>
注解驱动开发
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
/**
* @author Tellsea
* @date 2020-8-13
*/
@Slf4j
@ControllerAdvice
@Order(value = Ordered.HIGHEST_PRECEDENCE)
public class GlobalExceptionHandle {
@ExceptionHandler(value = Exception.class)
public String exception(Exception e, Model model) {
model.addAttribute("code", e.getClass());
model.addAttribute("info", e.getMessage());
log.error("【错误原因】{}", e.getClass());
log.error("【错误描述】{}", e.getMessage());
e.printStackTrace();
return "admin/error";
}
}
(13)案例:配置swagger静态资源访问放行
传统开发
<!-- 允许swagger界面-->
<mvc:resources location="classpath:/META-INF/resources/" mapping="swagger-ui.html"/>
<mvc:resources location="classpath:/META-INF/resources/webjars/" mapping="/webjars/**"/>
注解驱动开发
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* @author Tellsea
* @date 2020-8-13
*/
@Configuration
public class SwaggerConfig implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
}
}
3、测试类的入口方式转变
下面以读取IOC容器中所有bean为例,检查测试类是否可用
传统开发
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @author Tellsea
* @date 2020-8-13
*/
public class XmlTest {
public static void main(String[] args) {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
String[] definitionNames = applicationContext.getBeanDefinitionNames();
for (String name : definitionNames) {
System.out.println(name);
}
}
}
注解驱动开发
需要先定一个配置扫描类,然后注解驱动加载这个扫描类得到IOC容器
import org.springframework.context.annotation.ComponentScan;
/**
* @author Tellsea
* @date 2020-8-13
*/
@ComponentScan("cn.tellsea")
public class AppConfig {
}
import cn.tellsea.AppConfig;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
/**
* @author Tellsea
* @date 2020-8-13
*/
public class AnnonTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
String[] definitionNames = applicationContext.getBeanDefinitionNames();
for (String name : definitionNames) {
System.out.println(name);
}
}
}