赞
踩
提示:这里可以添加学习目标
例如:
提示:养成自学好习惯和攻克难关能力
学习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里面导入坐标)
<!--步骤1:导入Spring坐标-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
//步骤2:定义Spring管理的类(接口)
public interface BookService{
void save();
}
//实现接口
public class BookServiceImpl implements BookService{
private BookDao bookDao = new BookDaoImpl();
public void save(){
bookDao.save();
}
}
<!--步骤3:创建Spring配置文件,配置对应作为Spring管理的bean-->
<?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="bookDao" class="com.hcx.dao.impl.BookDaoImpl"/>
<bean id="bookService" class="com.hcx.service.impl.BookServiceImpl"/>
<!--id不能重复-->
//步骤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方法");
}
}
<!--步骤2:配置bean进行依赖注入,使用property标签注册一个属性-->
<bean id="bookDao" class="com.hcx.dao.impl.BookDaoImpl"/>
<bean id="bookService" class="com.hcx.service.impl.BookServiceImpl">
<!--配置dao与service的关系-->
<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">
<!--配置dao与service的关系-->
<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>100</value>
<value>200</value>
<value>300</value>
</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.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
配置数据源对象作为spring管理的bean
<!-- 管理DruidDataSource对象-->
<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>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<!-- 管理c3p0对象-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/message"/>
<property name="user" value="root"/>
<property name="password" value="505489"/>
</bean>
<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" system-properties-mode="NEVER"/>
<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>itcast</value> 集合注入简单类型
<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{
}
<?xml version="1.0" encoding="UTF-8"?>
<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
<context:component-scan/>
纯注解开发
@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> <!--初始化dataSource--> <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.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</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坐标
<!--1.导入坐标springmvc和servlet的-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.10.RELEASE</version>
</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'}";
}
请求参数
<!--json数据处理-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.4</version>
</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<User>参数传递 ==>" + list); return "{'module':'List<User>Json 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;
});
},
总结
创建工程,并导入坐标
<?xml version="1.0" encoding="UTF-8"?> <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.0</modelVersion> <groupId>org.example</groupId> <artifactId>springmvc_08_ssm</artifactId> <version>1.0-SNAPSHOT</version> <packaging>war</packaging> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.12</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.2.10.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.2.10.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.2.10.RELEASE</version> </dependency> <!-- spring整合mybatis需要三个坐标: mybatis mybatis-spring mysql-connect-java --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.6</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>1.3.2</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> </dependency> <!-- 该项目中选择使用druid数据源--> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.2.11</version> </dependency> <!-- 测试用,不解释--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <!-- 做springmvc开发时,web容器要用到的--> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.5</version> <scope>provided</scope> </dependency> <!-- 实现json文件与各数据类型之间的互相转换--> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.9.0</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.tomcat.maven</groupId> <artifactId>tomcat7-maven-plugin</artifactId> <version>2.1</version> <configuration> <port>8080</port> <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会运行
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。