提示:这里可以添加学习目标
例如:
提示:养成自学好习惯和攻克难关能力
学习Spring+SpringMVC
提示:找了份单休的工作边打工边学习,花了一个半月的时间学到这里
例如:
提示:代码行数不记得
例如:
简化开发,降低企业级开发的复杂性
框架整合,高效整合其他计算,提高企业级应用开发与运行效率
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-onTECLoY-1669819301997)(https://ts1.cn.mm.bing.net/th/id/R-C.40d21cdfc59cb8a652614cc7cfe0fd87?rik=4Bx8Us0bCDQ0FQ&riu=http%3a%2f%2fstatic.oschina.net%2fuploads%2fspace%2f2014%2f1231%2f111732_x0O8_1249631.png&ehk=nRnxwQgIJeO4XeIMAlfz2p7qAZwAyNSQ8Y%2fJoPmvIeI%3d&risl=&pid=ImgRaw&r=0)]
管理什么?(Service与Dao)
如何将被管理的对象告知IoC容器?(配置)【将bean注册到application.xml里面】【通过xml管理IoC容器】
被管理的对象交给IoC容器,如何获取到IoC容器?(接口)
一般,通过ApplicationContext接口中的实现类ClassPathXmlApplicationContext()
IoC容器得到后,如何从容器中获取bean?(接口方法)
通过ApplicationContext接口中的getBean方法
使用Spring导入哪些坐标?(pom.xml,在depencies里面导入坐标)
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>5.2.10.RELEASEversion>
dependency>
//步骤2:定义Spring管理的类(接口)
public interface BookService{
void save();
}
//实现接口
public class BookServiceImpl implements BookService{
private BookDao bookDao = new BookDaoImpl();
public void save(){
bookDao.save();
}
}
<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="bookDao" class="com.hcx.dao.impl.BookDaoImpl"/>
<bean id="bookService" class="com.hcx.service.impl.BookServiceImpl"/>
//步骤1:将业务层中的new的DAO对象删除,使用set方法实现
public class BookServiceImpl implements BookService{
private BookDao;
public void save(){
bookDao.save();
}
//提供对应的set方法
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
System.out.println("提供了对应的set方法");
}
}
<bean id="bookDao" class="com.hcx.dao.impl.BookDaoImpl"/>
<bean id="bookService" class="com.hcx.service.impl.BookServiceImpl">
<property name="bookDao" ref="bookDao"/>
bean>
在application.xml起别名,或取多个别名,【用空格,或者逗号,或者分号分隔开来】
ref
属性既可以引用id
也可以引用name
<bean id="bookDao" name="dao dao2" class="com.hcx.dao.impl.BookDaoImpl"/>
<bean id="bookService" name="service" class="com.hcx.service.impl.BookServiceImpl">
<property name="bookDao" ref="dao"/>
bean>
public static void main(String[] args) {
ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");
BookService bookService= (BookService) context.getBean("service");
bookService.save();
}
//把getBean方法里的名称换为bean的别名也是可以的
注意:获取bean无论是通过id还是name获取,如果无法获取到,将抛出NoSuchBeanDefinitionException
在application.xml中配置bean的作用范围
属性:scope
所属:bean标签
功能:定义bean的作用范围:
范例:
<bean id="bookDao" class="com.hcx.dao.impl.BookDaoImpl" scope="prototype"/>
bean
默认的是单例的? —便于管理复用对象,提高效率
适合交给容器进行管理的bean
//首先在application中注册bean
//其次在实现类中实现构造方法实例化实现类
private BookDaoImpl(){
System.out.println("Book is constructor is running ...");
}
//无论是使用private还是public都能访问到构造方法,利用到了反射
//无参构造方法是默认实现
<bean id="bookkDao" class="com.hcx.dao.impl.BookDaoImpl"/>
//新建一个静态工厂
public class OrderDaoFactory{
public static OrderDao getOrderDao(){
return new OrderDaoImpl();
}
}
//实例化对象时getBean()方法调用静态工厂实例化对象
<bean id="orderDao" class="com.hcx.factory.OrderDaoFactory" factory-method="getOrderDao"/>
public class AppForInstanceUser{
public static void main(String[] args){
//创建实例静态工厂对象
UserDaoFactory userDaoFactroy = new UserDaoFactory();
//通过实例工厂对象创建对象
User userDao = userDaoFactory.getUserDao();
userDao.save();
}
}
public class UserDaoFactory{
public UserDao getUserDao(){
retrun new UserDaoImpl();
}
}
<bean id="userFactory" class="com.hcx.factory.UserDaoFactory"/>
<bean id="userDao" factory-method="getUserDao" factory-bean="userFactory"/>
public class UserDaoFactoryBean implements FactoryBean<UserDao>{
public UserDao getObject() throws Expection{
return new UserDaoImpl();
}
public Class<?> getObjectType(){
return UserDao.class;
}
}
<bean id="userDao" class="com.hcx.factory.UserDaoFactoryBean"/>
public void init(){
System.out.print("Bean初始化操作");
}
public void destory(){
System.out.print("Bean销毁操作");
}
<bean id="bookDao" class="com.hcx.impl.BookDaoImpl" init-method="init" destory-method="destory"/>
//无法调用销毁方式原因,JVM直接退出,未执行关闭ClassPathXmlApplicationContext对象
eg:
// 不用 ApplicationContext接口下ClassPathXmlApplication实现类型下的close()方法 ,还可以使用使用ApplicationContext下的实现类AnnotationConfigApplicationContext()实现下的close()方法
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
BookDao bookDao =(BookDao) context.getBean("bookDao");
//暴力方法
context.close();
//灵巧方法
context.registerShutdownHook();
//bean生命周期,标准实现方式,实现InitializingBean, DisposableBean两个接口中的方法
eg:
public class BookServiceImpl implements BookService, InitializingBean, DisposableBean {
// 删除业务层中使用new的方式创建dao对象
// private BookDao bookDao = new BookDaoImpl();
private BookDao bookDao;
public void save() {
System.out.println("book service save ...");
bookDao.save();
}
//提供对应的set方法
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
System.out.println("提供了对应的set方法");
}
@Override
public void destroy() throws Exception {
System.out.println("service destory");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("service init");
}
}
bean生命周期
初始化容器
使用bean
关闭/销毁容器
bean销毁时机
容器关闭前触发bean的销毁
关闭容器方式
手工关闭容器
ConfigurableApplicationContext接口close()
操作
注册关闭钩子,在虚拟机退出前线关闭容器再退出虚拟机
ConfigurableApplicationContext接口registerShutdownHook()
操作
配置
接口
InitializingBean
DisposableBean
在bean中定义引用类型属性并提供可访问的set方法
public class BookServiceImpl implements BookService(
private BookDao bookDao;
public void setBookDao(BookDao bookDao){
this.bookDao = bookDao;
}
)
配置中使用property标签****value属性注入引用类型对象
<bean id="bookService" class="com.hcx.service.impl.BookServiceImpl">
<property name="bookDao" ref="bookDao"/>
bean>
<bean id="bookDao" class="com.hcx.dao.impl.BookDaoImpl"/>
public class BookDaoImpl implements BookDao(
private int connectionNum;
private String databaseName;
public void setConnectionNum(int connectionNum){
this.connectionNum=connectionNum;
}
public void setDatabaseName(String databaseName){
this.databaseName=datanaseName;
}
)
<bean id="bookDao" class="com.hcx.dao.impl.BookDaoImpl">
<property name="connectionNum" value="100"/>
<property name="databaseName" value="MySql"/>
bean>
public class BookServiceImpl implements BookService(
private BookDao bookDoa;
public BookServiceImpl(BookDao bookDao){
this.bookDao = bookDao;
}
)
<bean id="bookService" class="com.hcx.service.impl.BookServiceImpl">
<constructor-arg name="bookDao" ref="bookDao"/>
bean>
<bean id="bookDao" class="com.hcx.dao.impl.BookDaoImpl"/>
public class BookDaoImpl implements BookDao{
private int connectionNumber;
public BookDaoImpl(int connectionNumber){
this.connectionNumber = connectionNumber;
}
}
<bean id="bookDao" class="com.hcx.dao.impl.BookDaoImpl">
<constructor-arg name="connectionNumber" value="10"/>
bean>
参数设置
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
<constructor-arg type="int" value="10"/>
<constructor-arg type="java.lang.String" value="mysql"/>
bean>
<bean id="bookDao" class="com.hcx.dao.impl.BookDaoImpl">
<constructor-arg index="0" value="10"/>
<constructor-arg index="1" value="MYSQL"/>
bean>
IoC容器根据bean所依赖的资源在容器中自动查找并注入到bean中的过程称之为自动装配
自动装配方式
配置中使用bean标签autowire属性设置自动装配的类型
<bean id="bookDao" class="com.hcx.dao.impl.BookDaoImpl"/>
<bean id="bookService" class="com.hcx.service.impl.BookServiceImpl" autowire="byType"/>
数据类型:
数组
<property name="array">
<array>
<value>100value>
<value>200value>
<value>300value>
array>
property>
List
<property name="list">
<list>
<value>加油value>
<value>每天value>
<value>学习value>
list>
property>
Set
<property name="set">
<set>
<value>好好value>
<value>工作value>
<value>跑路value>
set>
property>
Map
<property name="map">
<map>
<entry key="country" value="china">entry>
<entry key="city" value="成都">entry>
<entry key="province" value="四川">entry>
map>
property>
Properties
<property name="properties">
<props>
<prop key="country">中国prop>
<prop key="city">南昌prop>
<prop key="province">江西prop>
props>
property>
导入druid坐标
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druidartifactId>
<version>1.1.10version>
dependency>
配置数据源对象作为spring管理的bean
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/message"/>
<property name="username" value="root"/>
<property name="password" value="505489"/>
bean>
<dependency>
<groupId>c3p0groupId>
<artifactId>c3p0artifactId>
<version>0.9.1.2version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.6version>
dependency>
<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
">
beans>
2.使用context空间加载properties文件
<context:property-placeholder location="jdbc.properties"/>
3.使用属性占位符${}读取properties文件中的属性
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.Driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
bean>
<context:property-placeholder location="jdbc.properties,msg.properties"/>
<context:property-placeholder location="*.properties"/>
<context:property-placeholder location="classpath:*.properties"/>
<context:property-placeholder location="classpath*:*.properties"/>
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
ApplicationContext ctx = new FileSystemXmlApplicationContext("D:\\applicationContext.xml");
XmlWebApplicationContext ctx = new XmlWebApplicationContext();
ApplicationContext ctx = new ClassPathXmlApplicationContext("bean1.xml","bean2.xml");
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
BookDao bookDao = ctx.getBean("bookDao",BookDao.class);
BookDao bookDao = ctx.getBean(BookDao.class);
总览 BeanFactory
体系,按照接口的抽象层次,大体可以分层四层:
BeanFactory
;HierarchicalBeanFactory
,ListableBeanFactory
,AutowireCapableBeanFactory
;ConfigurableBeanFactory
,此外还有一个关联性较强SingletonBeanRegistry
;ConfigurableListableBeanFactory
;Resource resources = new ClassPathResource("applicationContext.xml");
BeanFactory bf = new XmlBeanFactory(resources);
BookDao bookDao = bf.getBean("bookDao",BookDao.class);
bookDao.save();
注意:BeanFactory创建完毕后,所有的bean均为延时加载
延时加载好处:当为了追求传输效率就会需要什么就再去创建什么时,就会体现出延迟加载的好处,有一个缓冲时间。
BeanFactory是IoC容器的顶层接口,初始化BeanFactory对象时,加载的bean延时加载
ApplicationContext接口是Spring容器的核心接口,初始化时bean立即加载
ApplicationContext接口提供基础的bean操作相关方法,通过其他接口扩展其功能
ApplicationContext接口常用初始化类
<bean
id="bookDao" bean的Id
name="dao bookDaoImpl daoImpl" bean的别名
class="com.itheima.dao.impl.BookDaoImpl" bean的类型,静态工厂,FactoryBean类
scope="singleton" 控制bean的实例数量
init-method="init" 生命周期初始化方法
destory-method="destory" 生命周期销毁方法
autowire="byType" 自动装配类型
factory-method="getInstance" bean工厂方法,应用于静态工厂或实例工厂
factory-bean="com.itheima.factory.BookDaoFactory" 实例工厂bean
lazy-init="true" 控制bean延时加载
/>
<bean id="bookSerbice" class="com.hcx.impl.BookServiceImpl">
<constructor-arg name="bookDao" ref="bookDao"/> 构造器注入引用类型
<constructor-arg name="userDao" ref="userDao"/>
<constructor-arg name="msg" value="WARN"/> 构造器注入简单类型
<constructor-arg type="java.lang.String" index="3" value="WARN"/> 类型匹配与索引匹配
<property name="bookDao" ref="bookDao"/> setter注入引用类型
<property name="userDao" ref="userDao"/>
<property name="msg" value="WARN"/> setter注入简单类型
<property name="names"> list集合
<list>
<value>itcastvalue> 集合注入简单类型
<ref bean="dataSource"/> 集合注入引用类型
list>
property>
bean>
@Component("bookDao")
public class BookDaoImpl implements BookDao{
}
@Component
public class BookServiceImpl implements BookService{
}
<context:component-scan base-package="com.hcx"/>
@Repository("bookDao")
public class BookDaoImpl implements BookDao{
}
@Service
public class BookServiceImpl implements BookService{
}
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.springframework.org/schema/beans/spring-beans.xsd">
<context:component-scan base-package="com.hcx"/>
beans>
被
@Configuration
@ComponentScan("com.hcx")
public class SpringConfig{
}
代替
@ComponentScan({"com.hcx.service","com.hcx.dao"})
//加载配置文件初始化容器(xml版)
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
//加载配置类初始化容器
ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
定义bean
@Component
@Controller
@Service
@Repository
纯注解开发
@Servcie
public class BookServiceImpl implements BookService{
@Autowired
private BookDao bookDao;
public void setBookDao(BookDao bookDao){
this.bookDao=bookDao;
}
public void save(){
System.out.print("book service save ...");
bookDao.save();
}
}
注意:自动装配基于反射设计创建对象并暴力反射对应属性初始化数据,因此无需提供setter()方法
注意:自动装配建议使用无参构造方法创建对象(默认),如果不提供对应构造方法,请提供唯一的构造方法
使用@Qualifier注解开启指定名称装配bean
@Service
public class BookServiceImpl implements BookService{
@Autowired
@Qualifier("bookDao")
private BookDao bookDAO;
}
在定义的数据类型上放使用@Value("值")
@Value("学习")
private String action;
加载外部properties文件
@Configuration
@ComponentScan("com.hcx")
@uPropertySource("classpath:jdbc.properties")
public class SpringConfig{}
@Value("${外部文件中定义的属性名}")
private String value;
@Configuration
public class SpringConfig{
@Bean
public DataSource dataSource(){
DruidDataSource ds = new DruidDataSource();
ds.setDriverClassName("com.mysql.jdbc.Driver");
ds.setUrl("jdbc:mysql://localhost:3306/spring_db");
ds.setUsername("root");
ds.setPassword("root");
return ds;
}
}
//不推荐
public class JdbcConfig{
@Bean
public DataSource dataSource(){
DruidDataSource ds = new DruidDataSource();
//相关配置
return ds;
}
}
@Configuration
@Import(JdbcConfig.class)
public class SpringConfig{
}
public class JdbcConfig{
@Value("com.mysql.jdbc.Driver")
private String dirver;
@Value("jdbc:mysql://localhost:3306/spring_db")
private String url;
@Value("root")
private String userName;
@Value("505489")
private String password;
@Bean
public DataSource dataSource(){
DruidDataSource ds = new DruidDatSource();
ds.setDriverClassName(driver);
ds.setUrl(url);
ds.setUserName(userName);
ds.setPassword(password);
return ds;
}
}
@Bean
public DataSource dataSource(BookService bookService){
Sytstem.out.print(bookService);
DruidDataSource ds = new DruidDataSource();
//属性设置
return ds;
}
功能 | XML配置 | 注解 |
---|---|---|
定义bean | bean标签:id属性 class属性 | @ComponentScan @Component:@Controller @Service @Repository |
设置依赖注入 | setter注入(set方法):引用/简单 构造器注入(构造方法):引用/简单 自动装配 | @Autowired:@Qualifier @Value |
配置第三方bean | bean标签 静态工厂,实例工厂,FactoryBean | @Bean |
作用范围 | scope属性 | @Scope |
生命周期 | 标准接口:init-method destory-method | @PostConstryctor @ProDestory |
//1.创建SqlSessionFactoryBuilder对象
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilde();
//2.加载SqlMapConfig.xml配置文件
InpiyStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
//3.创建SqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
/*初始化SqlSessionFactroy*/
//4.获取SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
//5.执行SqlSession对象执行查询,获取结果User
AccountDao accountDao = sqlSession.getMapper(AccountDao.class);
/*获取实现*/
Account ac = accountDao.findById(2);
System.out.printIn(ac);
/*获取数据层接口*/
//6.释放资源
sqlSession.close();
/*关闭连接*/
<configuration>
<properties resource="jdbc.properties">properties>
<typeAliases>
<package name="com.itheima.domain"/>
typeAliases>
<environments default="mysql">
<environment id="mysql">
<transactionManager type="JDBC"> <dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}">property>
<property name="url" value="${jdbc.url}">property>
<property name="username" value="${jdbc.username}">property>
<property name="password" value="${jdbc.password}">property>
dataSource>
transactionManager>
environment>
environments>
<mappers>
<package name="com.itheima.do">package>
mappers>
configuration>
@Bean
public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource){
SqlSessionFactryBean ssfb = new SqlSessionFactoryBean();
ssfb.setTypeAliasePackage("com.hcx.pojo");
ssfb.setDataSource(dataSource);
return ssfb;
}
@Bean
public MapperScannerConfigurer mapperScannerConfigurer(){
MapperScannerConfigurer msc = new MapperScannerConfigurer();
msc.setBasePackage("com.hcx.dao");
return msc;
}
@RunWith(SpringJUnit4ClassRunner.class)
@ConetextConfiguration(classes = SpringConfig.class)
public Class BookServiceTest{
@Autowired
private BookService bookService;
@Test
public void testSave(){
bookservice.save();
}
}
<dependency>
<groupId>org.aspectjgroupId>
<artifactId>aspectjweaverartifactId>
<version>1.9.4version>
dependency>
说明:Spring-context坐标依赖spring-aop坐标
2.定义dao接口与实现类
public interface BookDao{
public void save();
public void update();
}
@Repository
public class BookDaoImpl implements BookDao{
public void save(){
System.out.println(System.currentTimeMills());
System.out.println("book dao save...");
}
public void update(){
System.out.println("book dao update...");
}
}
3.定义通知类,制作通知
public class MyAdvice{
public void before(){
System.out.println(System.currentTimeMills());
}
}
4.定义切入点
public class MyAdvice{
@Pointcut("execution(void com.hcx.dao.BookDao.update())")
private void pt(){}
}
说明:切入点定义依托一个不具有实际意义的方法进行,即无参数,无返回值,方法体无实际逻辑
5.绑定切入点与通知关系,并指定通知添加到原始连接点的具有执行位置
public class MyAdvice{
@Pointcut("execution(void com.itheima.dao.BookDao.update())")
private void pt(){}
@Before("pt()")
public void before(){
System.out.println(System.currenTimeMillis());
}
}
6.定义通知类受Spring容器管理,并定义当前类为切面类
@Componet
@Aspect
public class MyAdvice{
@Pointcut("execution(void com.hcx.dao.BookDao.update())")
private void pt(){}
@Before("pt()")
public void before(){
System.out.println(System.currentTimeMillis());
}
}
7.开启Spring对AOP注解驱动支持
@Configuration
@ComponetScan("com.hcx")
@EnableAspectAutoProxy
public class SpringConfig{
}
1.Spring容器启动
2.实现所有切面配置中的切入点
@Component
@Aspect
public class MyAdvice{
@Pointcut("excution(void com.hcx.dao.BookDao.save())")
private void ptx(){}
@Pointcut("excution(void com.hcx.dao.BookDao.update())")
private void pt(){}
@Before("pt()")
public void method(){
System.out.println(System.currentTimeMillis());
}
}
3.初始化bean,判定bean对应的类中的方法是否匹配到任意切入点
4.获取bean执行方法
public Interface BookDao{
public void update();
}
public class BookDaoImpl implements BookDao{
public void update(){
System.out.println("book dao update ...");
}
}
描述方式一:执行com.hcx.dao包下的BookDao接口中的无参数update方法
execution(void com.hcx.dao.BookDao.update())
描述方法二:执行com.hcx.dao.impl包下的BookDaoImpl类中的无参数update方法
execution(void com.hcx.iml.BookDaoImpl.update())
切入点表达式标准格式:动作关键字(访问修饰符,返回值,包名,类/接口.方法名(参数)异常名)
execution(public User com.hcx.service.UserService.findById(int))
动作关键字:描述切入点的行为动作,例如excution
表示执行到指定切入点
访问修饰符:public,private 等,可以省略
返回值
包名
类/接口名
方法名
参数
异常名:方法定义中抛出异常,可以省略
AOP通知描述了抽取的共性功能,根据共性功能抽取位置不同,最终运行代码时要将其加入到合理的位置
AOP通知分为5种类型
名称:@Around(重点,常用)
类型:方法注释
位置:通知方法定义上方
作用:设置当前通知方法与切入点之间的绑定关系,当通知方法在原始切入点方法前后运行
范例:
@Around("pt()")
public Object around(ProceedingJoinPoint pjp) throws Throwable{
System.out.println("around before advice ...");
Object ret = pjp.proceed();
System.out.println("arround after advice ...");
return ret;
}
@Around注意事项
@Around("pt()")
public Object around(ProceedingJoinPoint pjp) throws Throwable{
System.out.println("around before advice ...");
Object ret = pjp.proceed();
System.out.println("around after advice ...");
return ret;
}
名称:@AfterReturning(了解)
类型:方法注解
位置:通知方法定义上方
作用:设置当前通知方法与切入点之间的绑定关系,当前通知方法在原始切入点方法正常执行完毕后运行
范例:
@AfterReturning("pt()")
public void after Returning(){
System.out.println("afterReturning advice ...");
}
相关属性:value(默认):切入点方法名,格式为类名.方法名()
名称:@AfterThrowing(了解)
类型:方法注解
位置:通知方法定义上方
作用:设置当前通知方法与切入点之间的绑定关系,当前通知方法在原始切入点方法运行抛出异常后执行
范例:
@AfterThorwing("pt()")
public void afterThorwing(){
System.out.println("afterThrowing advice ...");
}
相关属性:value(默认):欺辱点方法名,格式为:类名.方法名()
步骤一:
@Around("ProjectAdvice.serbicePt()")
public void runSpeed(ProceedingJoinPoint pjp) throws Throwable{
long start = System.currentTimeMillis();
//获取执行签名信息
Singnature signature= pjp.getSignature();
//通过签名获取执行类型(接口名)
Stirng className = signature.getDeclaringTypeName();
String methodName = signature.getName();
for(int i= ; i< 10000;i++){
pjp.proceed();
}
long start =System.currentTimeMillis();
System.out.println("业务层接口万次执行"+className+"."+methodnName+"时间
:"+(end-start))+"ms"
}
需求:任意业务层接口军可执行显示器执行效率(执行时长)
分析:
获取切入点方法的参数
获取切入点方法返回值
获取切入点方法运行异常信息
JointPoint对象描述了连接点方法的运行状态,可以获取到原始方法的调用参数
@Before("pt()")
public void before(JointPoint jp){
Object[] args = jp.getArgs();
System.out.println(Arrays.toString(args));
}
public Object around(ProceedingJointPoint pjp) threos Throwable{
Object[] args = pjp.getArgs();
System.out.println(Arrays.toString(args));
Object ret = pjp.proceed();
return ret;
}
@AfterReturning(Value = "pt()",returning = "ret")
public void afterReturning(String ret){
System.out.println("afterRetruning advice ..."+ ret);
}
@Around("pt()")
public Object around(ProceedingJoinPoint pjp) throws Throwable{
Object ret = pjp.proceed();
return ret;
}
@AfterThrowing(value = "pt()",throwing ="t")
public void afterThrowing(Throwable t){
System.out.println("afterThrowing adivce ..."+ t);
}
@Around("pt()")
public Object around(ProceedingJoinPoint pjp){
Object ret = null;
try{
ret= pjp.proceed();
}catch(Throwable t){
t.printStackTrace();
}
return ret;
}
AOP通知获取数据
案例:百度网盘分析链接输入密码数据错误兼容性处理
分析:
@Around("DataAdvice.servicePt()")
public Object trimString(ProceedingJoinPoint pjp) throws Throwable{
Object[] args = pjp.getArgs();
//对原始参数的每一个参数进行操作
for(int i = 0; i < args.leng ; i++){
//如果是字符串数据
if(args[i].getClass().equals(String.class)){
//取出数据,trim()操作后,更新数据
args[i] = args[i].toString().trim();
}
}
return pjp.proceed(args);
}
excution(* com.hcx.service.*Service.*(..))
public interface PlatformTransactionManager{
void commit(TransactionStatus status) throws TransactionException;
void rollback(TransactionSattus status) throws TransactionException;
}
public class DataSourceTransactionManager{
...
}
需求:实现任意两个账户间转账操作
需求微缩:A账户减钱,B账户加钱
分析:
结果分析:
在业务层接口上添加Spring事务管理
public interface AccountService{
@Transactional
public void transfer(String out,String in,Double money);
}
注意:Spring注解式事务通常添加在业务层接口中而不会添加到业务层实习类中,降低耦合度
注解式业务可以添加到业务方法上表示开启事务,也可以添加到接口上表示当前接口所有方法开启事务
设置事务管理
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSoure){
DataSouceTransactionManager ptm = new DataSourceTransactionManager();
ptm.setDataSource(dataSource);
return ptm;
}
注意:事务管理要根据实现技术进行选择
MyBatis框架使用JDBC事务
开启注解事务驱动
@Configuration
@ComponentScan("com.hcx")
@PropertySource("classpath:jdbc.properties")
@Import({JdbcConfig.class,MybatisConfig.class})
@EnableTransactionManagement
public class SpringConfig{
}
注意:IOException不属于运行时异常,事务回滚不管理
事务传播行为类型 | 说明 |
---|---|
PROPAGATION_REQUIRED | 如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。 |
PROPAGATION_SUPPORTS | 支持当前事务,如果当前没有事务,就以非事务方式执行。 |
PROPAGATION_MANDATORY | 使用当前的事务,如果当前没有事务,就抛出异常。 |
PROPAGATION_REQUIRES_NEW | 新建事务,如果当前存在事务,把当前事务挂起。 |
PROPAGATION_NOT_SUPPORTED | 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 |
PROPAGATION_NEVER | 以非事务方式执行,如果当前存在事务,则抛出异常。 |
PROPAGATION_NESTED | 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。 |
需求:实现任意两个账户间转账操作,并对每次转账操作在数据库进行留痕
需求微缩:A账户减钱,B账户加钱,数据库记录日志
分析:
实现效果预期:
无论转账是否成功,均进行转账操作的日志留痕
存在问题:
日志的记录与转账操作隶属于同一事务,同成功同失败
失效效果预取改进:
无论转账操作是否成功,日志必须保留
步骤
@Service
public class LogServiceImpl implements LogService{
@Autowired
private LogDao logDao;
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void log(String out,String in,Double money){
logDao.log("转账操作由"+out+"到"+in+",金额:"+money);
}
}
使用SpringMVC技术需要县导入SpringMVC坐标与Servlet坐标
<dependency>
<groupId>javax.servletgroupId>
<artifactId>javax.servlet-apiartifactId>
<version>3.1.0version>
<scope>providedscope>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webmvcartifactId>
<version>5.2.10.RELEASEversion>
dependency>
创建SpringMVC控制器类(等同于Servlet功能)
@Controller
public class UserController{
@RequestMapping("/save")
@ResponseBody
public String save(){
System.out.println("user save ...");
return "{'module':'springmvc'}";
}
}
初始化SpringMVC环境(同Spring环境),设定SpringMVC加载对应的bean
@Configuration
@ComponentScan("com.iteima.controller")
public class SpringMvcConfig{
}
初始化Servlet容器,加载SpringMVC环境,并设置SpringMVC技术处理请求
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.support.AbstractDispatcherServletInitializer;
//4.定义一个servlet容器启动的配置类,在里面加载spring的配置
public class ServletContainersInitConfig extends AbstractDispatcherServletInitializer {
//加载springMVC容器配置
@Override
protected WebApplicationContext createServletApplicationContext() {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.register(SpringMvcConfig.class);
return context;
}
//设置哪些请求归属springMVC处理
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
//加载spring容器配置
@Override
protected WebApplicationContext createRootApplicationContext() {
return null;
}
}
名称:@Controller
类型:类注解
位置:SpringMVC控制器定义上方
作用:设置SpringMVC的核心控制器bean
范例:
@Controller
public class UserController{
}
名称:@ResquestMapping
类型:方法注释
位置:SpringMVC控制器方法定义上方
作用:设置当前控制器方法请求访问路径
范例:
@ResquestMapping("/save")
public void save(){
System.out.println("user save ...");
}
相关属性
名称:@ResponseBody
类型:方法注释
位置:SpringMVC控制器方法定义上方
作用:设置当前控制器方法响应内容为当前返回值,无需解析
范例:
@RequestMapping("/save")
@ResponseBody
public String save(){
System.out.println("user save ...");
return "{'info':"springmvc"}";
}
SpringMVC入门程序开发总结(1+N)
AbstractDispatcherServletInitializer类是SpringMVC提供的快速初始化web3.0容器的抽象类
AbstractDispatcherServletInitializer提供三个接口方法供用户实现
createServletAppilcationContext()方法,创建servlet容器时,加载SpringMVC对应的bean并放入WebApplicationContext对象中,而WebApplicationContext的作用范围为ServletContext范围,即整个web容器范围
protected WebApplicationContext createServletApplicationContext(){
AnnotattionConfigWebApplicationContext context = new AnnotattionConfigWebAppklicationtContext();
context.register(SpringMvcConfig.class);
return context;
}
getServletMappings()方法,设定SpringMVC对应的请求映射路径,设置为/表示拦截所有请求,任意请求都将转入道SpringMVC进行处理
protected String[] getServletMapping(){
return new String[]("/");
}
createRootApplicationContext()方法,如果创建Servlet容器时需要加载飞SpringMVC对应的bean,使用当前方法进行,使用方式用createServletAppilcationContext()
protected WebApplicationContext createRootApplicationContext(){
return null;
}
名称:@ComponentScan
类型:类注解
范例:
@Configuration
@ConmponentScan(value="com.itheima",
excludeFilters = @ComponentScna.Filter(
type = FilterType.ANNOTATION,
classer = Controller.class
)
)
public class SpringConfig{}
属性
bean的加载格式
//4.定义一个servlet容器启动的配置类,在里面加载spring的配置
public class ServletContainersInitConfig extends AbstractDispatcherServletInitializer {
//加载springMVC容器配置
@Override
protected WebApplicationContext createServletApplicationContext() {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.register(SpringMvcConfig.class);
return context;
}
//设置哪些请求归属springMVC处理
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
//加载spring容器配置
@Override
protected WebApplicationContext createRootApplicationContext() {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.register(SpringConfig.class);
return context;
}
}
简化开发
public class ServletContainersInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer{
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[]{SpringMvcConfig.class};
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[0];
/* return new Class[]{SpringConfig.class}*/
}
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
}
名称:@RequestMapping
类型:方法注解 类注解
位置:SpringMVC控制器方法定义上方
作用:设置当前控制器方法请求访问路径,如果设置在类上统一设置当前方法请求访问路径前缀
范例:
@Controller
@RequestMapping("/user")
public class UserController{
@RequestMapping("/save")
@ResponseBody
public String save(){
System.out.println("user save ...");
return "{'module':'user save'}";
}
}
属性
为web容器添加过过滤器并指定字符集,Spring-web包中提供了专用的字符过滤器
//乱码处理
@Override
protected Filter[] getServletFilters() {
CharacterEncodingFilter filter = new CharacterEncodingFilter();
filter.setEncoding("UTF-8");
return new Filter[]{filter};
}
参数种类
普通参数:url 地址穿参,地址参数名与形参变量名相同,定义形参即可接收参数
@RequestMapping("/commoParam")
@ResponseBody
public String commonParam(String name,int age){
Stystem.out.println("普通参数传递 name ==> " + name );
System.out.println("普通参数 age ==> " + age);
return "{'module':'common param'}";
}
名称:@RequestParam
类型:形参注解
位置:SpringMVC控制器方法形参定义前面
作用:绑定请求参数与处理器方法形参间的关系
范例:
@RequestMapping("/commonParamDifferentName")
@ResponseBody
public String commonParamDifferentName(@RequestParam("name") String userName , int age){
System.out.println("普通参数 userName ==>" + userName);
System.out.println("普通参数 userName ==>" + age);
return "{'module':'common param different name'}";
}
参数:
POJO参数/嵌套的POJO参数:请求数名与形参对象属性名相同,定义POJP类型形参即可接收参数
//嵌套pojo参数
@RequestMapping("/saveObj")
@ResponseBody
public String saveObj(User user){
System.out.println("对象接收数据"+user);
return "{'module':'pojo contain pojo param'}";
}
请求参数
<dependency>
<groupId>com.fasterxml.jackson.coregroupId>
<artifactId>jackson-databindartifactId>
<version>2.12.4version>
dependency>
//3.创建springmvc的配置文件,加载controller对应的bean
@Configuration
//@ComponentScan("com.hcx.controller")
@ComponentScan(value = "com.hcx"/*, excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = Controller.class)*/)
@EnableWebMvc
public class SpringMvcConfig {
}
//集合参数:JSON格式
@RequestMapping("/listParamJson")
@ResponseBody
public String listParamJson(@RequestBody List<String> list){
System.out.println("集合参数传递 list ==>" + list);
return "{'module':'listJson param'}";
}
//POJO参数:JSON格式
@RequestMapping("/pojoParamJson")
@ResponseBody
public String pojoParamJson(@RequestBody User user){
System.out.println("pojo(json)参数传递 user ==>"+user);
return "{'module':'pojoJson param'}";
}
//对象集合类型:JSON格式
@RequestMapping("/listUserParamJson")
@ResponseBody
public String listUserParamJson(@RequestBody List<User> list){
System.out.println("List参数传递 ==>" + list);
return "{'module':'ListJson param'}" ;
}
名称:@EnableWebMvc
类型:配置类注解
位置:SpringMVC配置定义上方
作用:开启SpringMVC多项负责功能
范例:
//3.创建springmvc的配置文件,加载controller对应的bean
@Configuration
//@ComponentScan("com.hcx.controller")
@ComponentScan(value = "com.hcx"/*, excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = Controller.class)*/)
@EnableWebMvc
public class SpringMvcConfig {
}
名称:@RequestBody
类型:形参注解
位置:SpringMVC控制器方法形参定义前面
作用:将请求中请求体所包含的数据传递给请求参数,此注解一个处理器方法只能使用一次
范例:
@RequestMapping("/listParamForJson")
@ResponseBody
public String listParamForJson(@RequestBody List<String> likes){
System.out.println("list common(json)参数传递 list ==>"+ likes);
return "{'moudle':'list common for json param'}";
}
日期类型数据基于系统不同格式也不尽相同
接收形参是,根据不同的日期格式设置不同的接收方式
@RequestMapping("/dateParam")
@ResponseBody
public String dateParam(Date date,
@DateTimeFormat(pattern = "yyyy-MM-dd") Date date1,
@DateTimeFormat(pattern = "yyyy/MM/dd HH:mm:ss")Date date2){
System.out.println("参数传递 date ==>" + date);
System.out.println("参数传递 date(yyyy-MM-dd) ==>" + date1);
System.out.println("参数传递 date(yyyy/MM/dd HH:mm:ss) ==>" + date2);
retrun "{'module':'data param'}";
}
名称:@DateTimeFormat
类型:形参注解
位置:SpringMVC控制器方法形参前面
作用:设定日期时间型数据格式
范例:
@RequestMapping("/dateParam")
@ResponseBody
public String dateParam(@DateTimeFormat(pattern = "yyyy-MM-dd") Date date){
System.out.println("参数传递 date ==>" + date);
retrun "{'module':'data param'}";
}
属性:pattern = “日期时间格式字符串”
Converter接口
public interface Converter<S,T>{
@Nullable
T convert(S source);
}
@EnableWebMvc功能之一:根据类型匹配对应类型转换器
响应页面
响应数据
响应文本数据
@RequestMapping("/toText")
@ResponseBody
public String toText(){
return "response text";
}
响应json数据(对象转json)
//响应POJO对象数据
@RequestMapping("/toJsonPOJO")
@ResponseBody
public User toJsonPOJO(){
System.out.println("返回json对象数据");
User user = new User();
user.setName("hcx");
user.setAge(21);
user.setAddress(new Address("南昌","江西"));
return user;
}
响应对象集合转json数组
//响应POJO集合对象
@RequestMapping("/toJsonList")
@ResponseBody
public List<User> toJsonList(){
System.out.println("返回json对象集合");
User user= new User();
user.setName("开源协议");
user.setAge(21);
user.setAddress(new Address("南昌","江西"));
User userOne= new User();
userOne.setName("阿里巴巴开发手册");
userOne.setAge(15);
userOne.setAddress(new Address("杭州","浙江"));
List<User> userList = new ArrayList<>();
userList.add(user);
userList.add(userOne);
return userList;
}
名称:@ResponseBody
类型:方法注解
位置:SpringMVC控制器方法定义上方
作用:设置当前控制器方法响应内容为当前返回值,无需解析
范例:
@RequestMapping("/save")
@ResponseBody
public String save(){
System.out.print("Save...");
return "{'info':'springmvc'}";
}
HttpMessageConverter接口
public interface HttpMessageConverter<T> {
boolean canRead(Class<?> var1, @Nullable MediaType var2);
boolean canWrite(Class<?> var1, @Nullable MediaType var2);
List<MediaType> getSupportedMediaTypes();
T read(Class<? extends T> var1, HttpInputMessage var2) throws IOException, HttpMessageNotReadableException;
void write(T var1, @Nullable MediaType var2, HttpOutputMessage var3) throws IOException, HttpMessageNotWritableException;
}
REST(Representational State Transfer),表现形式状态转换
传统风格资源描述形式
http://localhost/user/getById?id=1
http://localhost/user/saveUser
REST风格描述形式
http://localhost/user/1
http://localhost/user
优点:
注意事项:
上述行为是约定方式,约定不是规范,可以打破,所以称REST风格,而不是REST规范
描述模块的名称通常使用复数,也就是加s的格式描述,表示此类资源,而非单个资源,例如:users,books,accounts…
设定http请求动作(动词)
@RequestMapping(value="/users",method =RequestMethod.POST)
@ResponseBody
public String save(@RequestBody User user){
System.out.println("user save..." + user);
return "{'module':'user save'}";
}
@RequestMapping(value="/users",method=RequestMethod.PUT)
@ResponseBody
publiic String update(@RequestBody User user){
System.out.println("user update ..."+ user);
return "{'module':'user update'}";
}
设置请求参数(路径变量)
@RequestMapping(value ="/users/{id}",method =RequestMethod.DELETE)
@ResponseBody
public String delete(@PathVariable Integer id){
System.out.println("user delete ..."+ id);
return "{'module':'user deleter'}";
}
名称:@PathVariable
类型:形参注解
位置:SpringMVC控制器方法形参定义前面
作用:绑定路径参数与处理器方法形参间的关系,要求路径参数名与形参名一一对应
范例:
@RequestMapping(value = "/users/{id}",method =RequestMethod.DELETE)
@ResponseBody
public String save(@PatVariable Integer id){
System.out.println("user delete ..." + id);
return "{'module':'user delete'}";
}
名称:@RequestMapping
类型:方法注解
位置:SpringMVC控制器定义上方
作用:设置当前控制器方法请求访问路径
范例:
@RequestMapping(value = "/users",method =RequestMethod.POST)
@ResponseBody
public String save(@RequestBody User user){
System.out.println("user save ..." + user);
return "{'module':'user save'}";
}
属性
@RequestMapping(value="/books",method = RequestMethod.POST)
@ResponseBody
public String save(@RequestBody Book book){
System.out.println("book save ..."+ book);
return "{'module':'book save'}";
}
@RequestMapping(value="/books",method = RequestMethod.PUT)
@ResponseBody
public String save(@RequestBody Book book){
System.out.println("book update ..."+ boook);
return "{'module':'book update'}";
}
名称:@RestController
类型:类注解
位置:基于SpringMVC的RESTful开发控制器类定义上方
作用:设置当前控制器类为RESTful风格,等同于@Controller与@ResponseBody两个注解组合功能
范例:
@RestControoler
public class BookController{
}
名称:@GetMapping @PostMapping @PutMapping @DeleteMapping
类型:方法注解
位置:基于SpringMVC的RESTful开发控制器方法定义上方
作用:设置当前控制器方法请求访问路径与请求动作,每种对应一个请求动作,例如@GetMapping对应Get请求
范例:
@GetMapping("/{id}")
public String getById(@PathVariable Integer id){
System.out.pringln("book getById ..." + id);
return "{'module':'book getById'}";
}
属性
制作SpringMVC控制器,并通过PostMan测试接口功能
@RestController
@RequestMapping("/books")
public Class BookController{
@PostMapping
public String save(@RequestBody Book book){
System.out.println("book save ==>" + book);
return "{'moudule':'book save success'}";
}
@GetMapping
public List<Book> getAll(){
List<Book> bookList = new ArrayList<>();
Book book = new Book();
book.setType("计算机");
book.setName("SpringMVC入门教程");
book.setDescription("小试牛刀");
bookList.add(book);
Book book1 = new Book();
book1.setType("计算机");
book1.setName("SpringMVC入门教程");
book1.setDescription("加油");
bookList.add(book1);
//模拟数据
return bookList;
}
}
设置对静态支援的访问通行
@Configuration
public class SpringMvcSupport extends WebMvcConfigurationSupport {
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
//当访问/pages/???不要走mvc,走/pages目录下的内容访问
registry.addResourceHandler("/pages/**").addResourceLocations("/pages/");
//放行js
registry.addResourceHandler("/js/**").addResourceLocations("/js/");
//放行css
registry.addResourceHandler("/css/**").addResourceLocations("/css/");
//放行插件
registry.addResourceHandler("/plugins/**").addResourceLocations("/plugins/");
}
}
前端页面通过异步提交访问后台控制器
//添加
saveBook(){
axios.post("/books",this.formData).then((res)=>{
});
},
//主页列表查询
getAll(){
axios.get("/books").then((res)=>{
this.dataList = res.data;
});
},
总结
创建工程,并导入坐标
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<groupId>org.examplegroupId>
<artifactId>springmvc_08_ssmartifactId>
<version>1.0-SNAPSHOTversion>
<packaging>warpackaging>
<properties>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
<maven.compiler.source>1.8maven.compiler.source>
<maven.compiler.target>1.8maven.compiler.target>
properties>
<dependencies>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>1.18.12version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webmvcartifactId>
<version>5.2.10.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-jdbcartifactId>
<version>5.2.10.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-testartifactId>
<version>5.2.10.RELEASEversion>
dependency>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
<version>3.5.6version>
dependency>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatis-springartifactId>
<version>1.3.2version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.47version>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druidartifactId>
<version>1.2.11version>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.12version>
<scope>testscope>
dependency>
<dependency>
<groupId>javax.servletgroupId>
<artifactId>servlet-apiartifactId>
<version>2.5version>
<scope>providedscope>
dependency>
<dependency>
<groupId>com.fasterxml.jackson.coregroupId>
<artifactId>jackson-databindartifactId>
<version>2.9.0version>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.tomcat.mavengroupId>
<artifactId>tomcat7-maven-pluginartifactId>
<version>2.1version>
<configuration>
<port>8080port>
<path>/path>
configuration>
plugin>
plugins>
build>
project>
SSM整合
Spring
SpringConfig
@Configuration
@ComponentScan("com.hcx")
@PropertySource("classpath:jdbc.properties")
@Import({JdbcConfig.class,MyBatisConfig.class})
public class SpringConfig{
}
MyBatis
MybatisConfig
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.context.annotation.Bean;
import javax.sql.DataSource;
public class MyBatisConfig {
@Bean
public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource){
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
factoryBean.setDataSource(dataSource);
factoryBean.setTypeAliasesPackage("com.hcx.domain");
return factoryBean;
}
@Bean
public MapperScannerConfigurer mapperScannerConfigurer(){
MapperScannerConfigurer mapperScannerConfigurer=new MapperScannerConfigurer();
mapperScannerConfigurer.setBasePackage("com.hcx.dao");
return mapperScannerConfigurer;
}
}
JdbcConfig
public class JdbcConfig{
@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String userName;
@Value("${jdbc.password}")
private String password;
@Bean
public DataSource dataSource(){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName(driver);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
return dataSource;
}
//开启事务管理
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource){
DataSourceTransactionManager ds = new DataSourceTransactionManager();
ds.setDataSource(dataSource);
return ds;
}
}
jdbc.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/ssm_db
jdbc.username=root
jdbc.password=******
SpringMVC
ServletConfig
package com.hcx.config;
import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
import javax.servlet.Filter;
public class ServletConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
protected Class<?>[] getRootConfigClasses() {
return new Class[]{SpringConfig.class};
}
protected Class<?>[] getServletConfigClasses() {
return new Class[]{SpringMvcConfig.class};
}
protected String[] getServletMappings() {
return new String[]{"/"};
}
//乱码处理——post表单提交时处理中文乱码
@Override
protected Filter[] getServletFilters() {
CharacterEncodingFilter filter = new CharacterEncodingFilter();
filter.setEncoding("UTF-8");
return new Filter[]{filter};
}
}
SpringMvcConfig
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
@Configuration
@ComponentScan("com.hcx.controller")
@EnableWebMvc
public class SpringMvcConfig {
}
功能模块
表与实体类
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class Book {
private Integer id;
private String type;
private String name;
private String description;
}
dao(接口+自动代理)
import com.hcx.domain.Book;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
import org.springframework.stereotype.Repository;
import java.util.List;
public interface BookDao {
/*写法一:@Insert("insert into tbl_book values(null,#{type},#{name},#{description})")*/
@Insert("insert into tbl_book (type,name,description) values(#{type},#{name},#{description})")
public void save(Book book);
@Update("update tbl_book set type= #{type},name = #{name},description = #{description} where id = #{id}")
public void update(Book book);
@Delete("delete from tbl_book where id = #{id}")
public void delete(Integer id);
@Select("select * from tbl_book where id = #{id}")
public Book getById(Integer id);
@Select("select * from tbl_book")
public List<Book> getAll();
}
service(接口+实现类)
import com.hcx.domain.Book;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Transactional
public interface BookService {
/**
* 保存
* @param book
* @return
*/
public Boolean save(Book book);
/**
* 修改
* @param book
* @return
*/
public Boolean update(Book book);
/**
* 根据id删除
* @param id
* @return
*/
public Boolean delete(Integer id);
/**
* 根据id查询
* @param id
* @return
*/
public Book getById(Integer id);
/**
* 查询全部
* @return
*/
public List<Book> getAll();
}
实现类
import com.hcx.dao.BookDao;
import com.hcx.domain.Book;
import com.hcx.service.BookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class BookServiceImpl implements BookService {
@Autowired
private BookDao bookDao;
public Boolean save(Book book) {
bookDao.save(book);
return true;
}
public Boolean update(Book book) {
bookDao.update(book);
return true;
}
public Boolean delete(Integer id) {
bookDao.delete(id);
return true;
}
public Book getById(Integer id) {
return bookDao.getById(id);
}
public List<Book> getAll() {
return bookDao.getAll();
}
}
业务层接口测试(整合JUnit)
import com.hcx.config.SpringConfig;
import com.hcx.domain.Book;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.util.List;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class BookServiceTest {
@Autowired
private BookService bookService;
@Test
public void testGetById(){
Book book = bookService.getById(1);
System.out.println(book);
}
@Test
public void testGetAll(){
List<Book> list=bookService.getAll();
System.out.println(list);
}
}
controller
import com.hcx.domain.Book;
import com.hcx.service.BookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/books")
public class BookController {
@Autowired
private BookService bookService;
@PostMapping
public Result save(@RequestBody Book book) {
boolean flag = bookService.save(book);
return new Result(flag ? Code.SAVE_OK : Code.SAVE_ERR, flag);
}
@PutMapping
public Result update(@RequestBody Book book) {
boolean flag = bookService.update(book);
return new Result(flag ? Code.UPDATE_OK : Code.UPDATE_ERR, flag);
}
@DeleteMapping("/{id}")
public Result delete(@PathVariable Integer id) {
boolean flag = bookService.delete(id);
return new Result(flag ? Code.DELETE_OK : Code.DELETE_ERR, flag);
}
@GetMapping("/{id}")
public Result getById(@PathVariable Integer id) {
Book book=bookService.getById(id);
Integer code = book != null ? Code.GET_OK:Code.GET_ERR;
String msg = book != null ? "数据查询成功" : "数据查询失败请重试";
return new Result(code,book,msg);
}
@GetMapping
public Result getAll() {
List<Book> bookList = bookService.getAll();
Integer code = bookList != null ? Code.GET_ALL_OK:Code.GET_ALL_ERR;
String msg = bookList != null ? "全部数据查询成功":"数据查询失败请重试";
return new Result(code,bookList,msg);
}
}
数据传输协议所要用到的bean
Result.java
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Result {
private Integer code;
private Object data;
private String msg;
public Result(Integer code, Object data) {
this.code = code;
this.data = data;
}
}
Code.java
public class Code {
public static final Integer SAVE_OK = 20011;
public static final Integer DELETE_OK = 20021;
public static final Integer UPDATE_OK = 20031;
public static final Integer GET_OK = 20041;
public static final Integer GET_ALL_OK = 20051;
public static final Integer SAVE_ERR = 20010;
public static final Integer DELETE_ERR = 20020;
public static final Integer UPDATE_ERR = 20030;
public static final Integer GET_ERR = 20040;
public static final Integer GET_ALL_ERR = 20050;
}
数据返回格式:
{
"code":Integer,
"data":Obj,
"msg":String
}
完成方式:
在Controller里面添加Result类和Code类,并让Controller里面的方法都返回Result类型的结果
异常处理器
各个层级均出现异常,异常处理代码书写在哪一层
——所有的异常均抛出到表现层处理
表现层处理异常,每个方法中单独书写,代码书写量巨大且意义不强,如何解决——AOP思想
定义异常处理器
@ResControllerAdvice
public class ProjectExceptionAdvice{
@ExceptionHandler(Exception.class)
public Result doExpetion(Exception ex){
System.out.println("异常被捕获");
return new Result(001,null,"异常被捕获");
}
}
注解使用:
名称:@RestControllerAdvice
类型:类注解
位置:Rest风格开发的控制器增强类定义上方
作用:为Rest风格开发的控制器类做增强
范例:
@ResControllerAdvice
public class ProjectExceptionAdvice{
}
说明:
名称:@ExceptionHandler
类型:方法注解
位置:专用于异常处理的控制器上方
作用:设定指定异常的处理方案,功能等同于控制器方法,出现异常后终止原始控制器执行,并转入当前方法执行
范例:
@RestControllerAdvice
public class ProjectExceptionAdvice{
@ExceptionHandler(Exception.class)
public Result doException(Exception exception){
return new Result(001,null,"异常已被捕获")
}
}
说明:
项目异常分类
项目异常处理方案
自定义项目系统级异常
public class SystemException extends RuntimeException{
private Integer code;
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public SystemException(Integer code) {
this.code = code;
}
public SystemException(Integer code,String message ) {
super(message);
this.code = code;
}
public SystemException(Integer code,String message, Throwable cause ) {
super(message, cause);
this.code = code;
}
public SystemException(Integer code,Throwable cause ) {
super(cause);
this.code = code;
}
public SystemException(Integer code,String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
this.code = code;
}
}
自定义项目业务级异常
public class BusinessException extends RuntimeException{
private Integer code;
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public BusinessException(Integer code) {
this.code = code;
}
public BusinessException(Integer code, String message ) {
super(message);
this.code = code;
}
public BusinessException(Integer code, String message, Throwable cause ) {
super(message, cause);
this.code = code;
}
public BusinessException(Integer code, Throwable cause ) {
super(cause);
this.code = code;
}
public BusinessException(Integer code, String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
this.code = code;
}
}
自定义异常编码(持续补充)
public class Code{
public static final Integer SAVE_OK = 20011;
public static final Integer DELETE_OK = 20021;
public static final Integer UPDATE_OK = 20031;
public static final Integer GET_OK = 20041;
public static final Integer GET_ALL_OK = 20051;
public static final Integer SAVE_ERR = 20010;
public static final Integer DELETE_ERR = 20020;
public static final Integer UPDATE_ERR = 20030;
public static final Integer GET_ERR = 20040;
public static final Integer GET_ALL_ERR = 20050;
public static final Integer SYSTEM_ERR = 20060;
public static final Integer SYSTEM_TIMEOUT_ERR = 20070;
public static final Integer BUSINESS_ERR = 20080;
public static final Integer SYSTEM_UNKNOW_ERR = 20090;
}
触发定义异常
@Service
public class BookServiceImpl implements BookService {
@Autowired
private BookDao bookDao;
public Boolean save(Book book) {
bookDao.save(book);
return true;
}
public Boolean update(Book book) {
bookDao.update(book);
return true;
}
public Boolean delete(Integer id) {
bookDao.delete(id);
return true;
}
public Book getById(Integer id) {
//模拟业务异常
if(id == 1){
throw new BusinessException(Code.BUSINESS_ERR,"访问连接过多异常");
}
//将可能出现的异常进行包装,转换称自定义异常
try {
int i = 1/0;
}catch (Exception exception){
throw new SystemException(Code.SYSTEM_TIMEOUT_ERR,"服务器访问超时,请重试",exception);
}
return bookDao.getById(id);
}
public List<Book> getAll() {
return bookDao.getAll();
}
}
拦截并处理异常
@RestControllerAdvice
public class ProjectExceptionAdvice {
@ExceptionHandler(Exception.class)
public Result doException(Exception exception) {
//记录日志(错误堆栈)
//发送消息给运维
//发送邮件给开发人员,exception对象发送给开发人员
System.out.println("其他异常被捕获");
return new Result(Code.SYSTEM_UNKNOW_ERR, null, "系统繁忙请稍后再试");
}
@ExceptionHandler(SystemException.class)
public Result doSystemException(SystemException exception) {
//记录日志(错误堆栈)
//发送消息给运维
//发送邮件给开发人员,exception对象发送给开发人员
System.out.println("系统异常被捕获");
return new Result(exception.getCode(), null, exception.getMessage());
}
@ExceptionHandler(BusinessException.class)
public Result doSystemException(BusinessException exception) {
System.out.println("业务异常被捕获");
return new Result(exception.getCode(), null, exception.getMessage());
}
}
异常处理效果对比
总结:
…
制作拦截器功能类
配置拦截器的执行位置
声明拦截器的bean,并实现HandlerInterCeptor接口(注意:扫描加载bean)
@Component
public class ProjectInterCeption implements HandlerInterceptor{
public boolean preHandle(..) throws Exception{
System.out.println("preHandle...");
return true;
}
public void postHandle(..) throws Exception{
System.out.println("postHandle...");
}
public void afterCompletion(..) throws Exception{
Syetm.out.println("afterCompletion...");
}
}
定义配置类,集成WebMvcConfigurationSupport,实现addInterceptor方法(注意:扫描加载配置)
@Configuration
public class SpringMvcSupport extends WebMvcConfigurationSupport{
@Override
public void addInterceptors(InterceptorRegistry registry){
...
}
}
添加拦截器并设置拦截访问路径,路径可以通过可变参数设置多个
@Configuration
public class SpringMvcSupport extends WebMvcConfigurationSupport{
@Autowired
private ProjectInterceptor projectInterceptor;
@Override
public void addInterCeptors(InterceptorRegisetry registry){
regisyry.addInterceptor(proejectInterceptor).addPathPatternas("/books");
}
}
使用标准接口WebMvcConfigurer简化开发(注意:侵入式较强)
@Configuration
@ComponentScan("com.hcx.controller")
@EnableWebMvc
public class SpringMvcConfig implements WebMvcConfigurer{
@Autowired
private ProjectInterCeptor projectInterceptor;
public void addInterceptors(InterceptorRegistry registry){
registry.addInterceptor(projectInterceptor).addPatternas("/books","/books");
}
}
前置处理
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle...");
return true;
}
具体参数:
handler:被调用的处理器对象,本质上是一个方法对象,对反射计数中的Method对象进行了再包装,可用于获取拦截方法信息
request:请求对象
response:响应对象
modelAndView:获取页面跳转相关数据
ex:拿到原始程序执行过程中出现的异常,表现层出现的异常
返回值
拦截器链配置方式
拦截器链的运行顺序
解释:顺序情况下的话,走到哪里出现错误返回false,则后面的postHandle拦截器都不运行,afterComletion会运行