目录
Spring系统框架
核心概念
●IoC(Inversion of Control)控制反转
●使用对象时,由主动w产生对象转换为由外部提供对象,此过程中对象创建控制权由程序转移到外部,此思想称为控制反转
●Spring技术对IoC思想进行了实现
●Spring提供了一个容器,称为IoC容器,用来充当IoC思想中的外部
●IoC容器负责对象的创建、初始化等一系列工作,被创建或被管理的对象在Ioc容器中统称为Bean ●DI(Dependency Injection)依赖注入
●在容器中建立bean与bean之间的依赖关系的整个过程,称为依赖注入
●目标:充分解耦
●使用IoC容器管理bean(IoC)
●在Ioc容器内将有依赖关系的bean进行关系绑定(DI)
●最终效果
●使用对象时不仅可以直接从Ioc容器中获取,并且获取到的bean已经绑定了所有的依赖关系
IoC入门(XML版)
①:导入Spring坐标
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
②:定义Spring管理的类(接口)
public interface BookService {
public void save();
}
public class BookServiceImpl implements BookService {
private BookDao bookDao=new BookDaoImpl();
public void save(){
System.out.println("book service save....");
bookDao.save();
}
}
③:创建Springi配置文件,配置对应类作为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">
<!-- 1.导入spring的坐标spring-context,对应版本是5.2.18.RELEASE-->
<!-- 配置bean-->
<!-- bean标签表示配置bean-->
<!-- id属性表示给bean起名字-->
<!-- cLass属性表示给bean定义类型-->
<bean id="bookDao" class="com.chj.dao.impl.BookDaoImpl"/>
<bean id="bookService" class="com.chj.service.impl.BookServiceImpl"/>
</beans>
注意事项
bean定义时id属性在同一个上下文中不能重复
④:初始化IoC容器(Spring核心容器/Spring容器),通过容器获取bean
public class App2 {
public static void main(String[] args) {
ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");
// BookDao bookdao = (BookDao) context.getBean("bookDao");
// bookdao.save();
BookService bookService = (BookService) context.getBean("bookService");
bookService.save();
}
}
DI入门案例(ML版)
①:删除使用new的形式创建对象的代码并提供对应的setter的方法
public class BookServiceImpl implements BookService {
//5.删除业务层中使用new的方式创建的dao对象
private BookDao bookDao;
public void save(){
System.out.println("book service save....");
bookDao.save();
}
//提供对应的set方法
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
}
}
②:配置service与dao之间的关系
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="bookDao" class="com.chj.dao.impl.BookDaoImpl"/>
<bean id="bookService" class="com.chj.service.impl.BookServiceImpl">
<!--7.配置server与dao的关系-->
<!--property标签表示配置当前bean的属性
name属性表示配置哪一个具体的属性
ref属性表示参照娜一个bean-->
<property name="bookDao" ref="bookDao"/>
</bean>
</beans>
name为service层中属性的名称,ref为xml中的bean的名称
name属性功能
定义bean的别名,可定义多个,使用逗号(,)分号(;)空格( )分隔
范例
<bean id="bookDao"name="dao bookDaoImpl"class="com.itheima.dao.impl.BookDaoImpl"/>
<bean name="service,bookServiceImpl"class="com.itheima.service.impl.BookServiceImpl"/>
注意事项
获取bean无论是通过id还是name获取,如果无法获取到,将抛出异常NoSuchBeanDefinitionException
NoSuchBeanDefinitionException:No bean named 'bookServiceImpl'available
scope属性功能
定义bean的作用范围,可选范围如下
●singleton:单例(默认)
●prototype:非单例
范例
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl" scope="prototype"/>
实例化bean的三种方式
实例化bean的三种方式一构造方法(常用)
●提供可访问的构造方法
public class BookDaoImpl implements BookDao {
public BookDaoImpl(){
System.out.println("book dao constructor is running");
}
@Override
public void save() {
System.out.println("book dao save.......");
}
}
●配置
<?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="spring02_bean_instance.dao.impl.BookDaoImpl" scope="singleton"/>
</beans>
●无参构造方法如果不存在,将抛出异常BeanCreationException
实例化bean的三种方式—静态工厂(了解)
静态工厂
public class OrderDaoFactory {
public static OrderDao getOrderDao(){
return new OrderDaoImpl();
}
}
配置
<?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-->
<!-- <bean id="bookDao" class="spring02_bean_instance.dao.impl.BookDaoImpl" scope="singleton"/>-->
<!-- 方式二:使用静态工厂实例化bean-->
<bean id="orderDao" class="spring02_bean_instance.factory.OrderDaoFactory" factory-method="getOrderDao"/>
</beans>
实例化bean的三种方式—实例工厂(了解)
实例工厂
public class UserDaoFactory {
public UserDao getUserDao(){
return new UserDaoImpl();
}
}
配置
<?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-->
<!-- <bean id="bookDao" class="spring02_bean_instance.dao.impl.BookDaoImpl" scope="singleton"/>-->
<!-- 方式二:使用静态工厂实例化bean-->
<!-- <bean id="orderDao" class="spring02_bean_instance.factory.OrderDaoFactory" factory-method="getOrderDao"/>-->
<!-- 方法三:使用实例化工厂实例化bean-->
<bean id="userFactory" class="spring02_bean_instance.factory.UserDaoFactory"/>
<bean id="userDao" factory-method="getUserDao" factory-bean="userFactory"/>
</beans>
实例化bean的第四种方式一FactoryBean
FactoryBean
public class UserDaoFactoryBean implements FactoryBean<UserDao> {
@Override
public UserDao getObject() throws Exception {
return new UserDaoImpl();
}
@Override
public Class<?> getObjectType() {
return UserDao.class;
}
}
配置
<bean id="userDao" class="spring02_bean_instance.factory.UserDaoFactoryBean"/>
bean生命周期
●生命周期:从创建到消亡的完整过程
●bean生命周期:bean从创建到销毁的整体过程
●bean生命周期控制:在bean创建后到销毁前做一些事情
bean生命周期控制
●提供生命周期控制方法
public class BookDaoImpl implements BookDao {
@Override
public void save() {
System.out.println("book dao save.......");
}
//表示bean初始化对应的操作
public void init(){
System.out.println("init ....");
}
//表示bean销毁前对应的操作
public void destroy(){
System.out.println("destroy ....");
}
}
●配置生命周期控制方法
<bean id="bookDao" class="com.chj.spring03_bean_lifecycle.dao.impl.BookDaoImpl" init-method="init" destroy-method="destroy"/>
bean生命周期控制----接口控制(了解)
public class BookServiceImpl implements BookService, InitializingBean, DisposableBean {
private BookDao bookDao;
public void save(){
System.out.println("book service save....");
bookDao.save();
}
//提供对应的set方法
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
}
@Override
public void destroy() throws Exception {
System.out.println("book service destroy....");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("book service init....");
}
}
bean销毁时机
●容器关闭前触发bean的销毁
●关闭容器方式:
■手工关闭容器
ConfigurableApplicationContext接☐close()操作
■注册关闭钩子,在虚拟机退出前先关闭容器再退出虚拟机
ConfigurableApplicationContext:接口registerShutdownHook()操作
public class App {
public static void main(String[] args) {
ClassPathXmlApplicationContext app=new ClassPathXmlApplicationContext("applicationContext.xml");
//注册关闭钩子
// app.registerShutdownHook();
BookDao bookDao = (BookDao) app.getBean("bookDao");
bookDao.save();
//手工关闭容器
app.close();
}
}
总结
●初始化容器
1.创建对象(内存分配)
2.执行构造方法
3.执行属性注入(set操作)
4.执行bean初始化方法
●使用bean
1.执行业务操作
●关闭/销毁容器
1.执行bean销毁方法
依赖注入方式
●思考:向一个类中传递数据的方式有几种?
■普通方法(set方法)
■构造方法
●思考:依赖注入描述了在容器中建立bean与bean之间依赖关系的过程,如果bean运行需要的是数字或字符串呢?
■引用类型
■简单类型(基本数据类型与String)
●依赖注入方式
■setter注入
◆简单类型
◆引用类型
■构造器注入
■简单类型
■引用类型
setter注入一引用类型
●在bean中定义引用类型属性并提供可访问的set方法
public class BookServiceImpl implements BookService {
private BookDao bookDao;
//提供对应的set方法
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
}}
●配置中使用property标签ref属性注入引用类型对象
<bean id="bookDao" class="com.chj.spring03_bean_lifecycle.dao.impl.BookDaoImpl"/>
<bean id="bookService" class="com.chj.spring03_bean_lifecycle.service.impl.BookServiceImpl" >
<!--7.配置server与dao的关系-->
<!--property标签表示配置当前bean的属性
name属性表示配置哪一个具体的属性
ref属性表示参照娜一个bean-->
<property name="bookDao" ref="bookDao"/>
</bean>
setter注入一简单类型
●在bean中定义引用类型属性并提供可访问的set方法
public class BookDaoImpl implements BookDao {
private String database;
public void setDatabase(String database) {
this.database = database;
}
@Override
public void save() {
System.out.println("book dao save......."+database);
}
}
●配置中使用property标签value属性注入简单类型数据
<bean id="bookDao" class="com.chj.dao.impl.BookDaoImpl" >
<property name="database" value="mysql"/>
</bean>
构造器注入一引用类型(了解)
●在bean中定义引用类型属性并提供可访问的构造方法
public class BookServiceImpl implements BookService {
private BookDao bookDao;
private UserDao userDao;
public BookServiceImpl(BookDao bookDao, UserDao userDao) {
this.bookDao = bookDao;
this.userDao = userDao;
}
public void save(){
System.out.println("book service save....");
bookDao.save();
userDao.save();
}
}
●配置中使用constructor-arg标签ref属性注入引用类型对象
<!--标准书写
<bean id="bookDao" class="chj.dao.impl.BookDaoImpl" >
<property name="database" value="mysql"/>
</bean>
<bean id="userDao" class="chj.dao.impl.UserDaoImpl" />
<bean id="bookService" class="chj.service.impl.BookServiceImpl" >
<constructor-arg name="bookDao" ref="bookDao" />
<constructor-arg name="userDao" ref="userDao"/>
</bean>-->
<!--解决形参名称的问题,与形参名不耦合
<bean id="bookDao" class="chj.dao.impl.BookDaoImpl" >
<constructor-arg name="database" value="mysql"/>
</bean>
<bean id="userDao" class="chj.dao.impl.UserDaoImpl" />
<bean id="bookService" class="chj.service.impl.BookServiceImpl" >
<constructor-arg name="bookDao" ref="bookDao" />
<constructor-arg name="userDao" ref="userDao"/>
</bean>-->
<!-- 解决参数类型重复问题,使用位置解决参数匹配-->
<bean id="bookDao" class="chj.dao.impl.BookDaoImpl" >
<constructor-arg index="0" value="mysql"/>
</bean>
<bean id="userDao" class="chj.dao.impl.UserDaoImpl" />
<bean id="bookService" class="chj.service.impl.BookServiceImpl" >
<constructor-arg name="bookDao" ref="bookDao" />
<constructor-arg name="userDao" ref="userDao"/>
</bean>
依赖注入方式选择
1.强制依赖使用构造器进行,使用setter注入有概率不进行注入导致nu11对象出现
2.可选依赖使用setteri注入进行,灵活性强
3.Spring框架倡导使用构造器,第三方框架内部大多数采用构造器注入的形式进行数据初始化,相对严谨
4.如果有必要可以两者同时使用,使用构造器注入完成强制依赖的注入,使用setteri注入完成可选依赖的注入
5.实际开发过程中还要根据实际情况分析,如果受控对象没有提供setter方法就必须使用构造器注入
6.自己开发的模块推荐使用setter注入
依赖自动装配
●Ioc容器根据bean所依赖的资源在容器中自动查找并注入到bean中的过程称为自动装配
●自动装配方式
■按类型(常用)
■按名称
■按构造方法
■不启用自动装配
配置中使用bean标签autowire属性设置自动装配的类型
<bean id="bookDao" class="com.chj.dao.impl.BookDaoImpl">
<constructor-arg name="database" value="mysql"/>
</bean>
<bean id="bookService" class="com.chj.service.impl.BookServiceImpl" autowire="byType" />
● 自动装配用于引用类型依赖注入,不能对简单类型进行操作
● 使用按类型装配时(byType)必须保障容器中相同类型的bean唯一,推荐使用
● 使用按名称装配时(byName)必须保障容器中具有指定名称的bean,因变量名与配置耦合,不推荐 使用
● 自动装配优先级低于setter注入与构造器注入,同时出现时自动装配配置失效
注入集合对象
注入数组对象
注入List对象
注入Set对象
注入Map对象
注入Properties对象
<bean id="bookDao" class="com.chj.dao.impl.BookDaoImpl">
<property name="array">
<array>
<value>100</value>
<value>200</value>
<value>300</value>
</array>
</property>
<property name="list">
<list>
<value>chj</value>
<value>aty</value>
<value>dsc</value>
</list>
</property>
<property name="set">
<set>
<value>chj</value>
<value>aty</value>
<value>dsc</value>
</set>
</property>
<property name="map">
<map>
<entry key="country" value="china"/>
<entry key="province" value="guangdong"/>
<entry key="city" value="shenzhen"/>
</map>
</property>
<property name="properties">
<props>
<prop key="country">china</prop>
<prop key="province">jiangxi</prop>
<prop key="city">nanchang</prop>
</props>
</property>
</bean>
案例:数据源对象管理
导入druid坐标
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.16</version>
</dependency>
配置数据源对象作为spring管理的bean
<bean id="datasource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/chj"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
加载properties文件
●开启context命名空间
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
</bean>
●使用context命名空间,加载指定properties文件
加载properties文件
<context:property-placeholder location="jdbc.properties"/>
不加载系统属性
<context:property-placeholder location="jdbc.properties"system-properties-mode="NEVER"/>
加载多个properties文件
<context:property-placeholder location="jdbc.properties,msg.properties"/>
加载所有properties?文件
<context:property-placeholder location="*.properties"/>
加载properties.文件标准格式
<context:property-placeholder location="classpath:*.properties"/>
从类路径或jar包中搜索并加载properties文件
<context:property-placeholder location="classpath*:*.properties"/>
●使用${}读取加载的属性值
<!-- 3.使用属性占位${}读取properties文件中的属性-->
<bean id="datasource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
容器
创建容器
public class App {
public static void main(String[] args) {
//●方式一:类路径加载配置文件
ApplicationContext ctx =new ClassPathXmlApplicationContext("applicationContext.xml");
//●方式二:文件路径加载配置文件
ApplicationContext ctx1 =new FileSystemXmlApplicationContext("F:\\java后端\\project\\spring10_container\\src\\main\\resources\\applicationContext.xml");
//●加载多个配置文件
ApplicationContext ctx2 =new ClassPathXmlApplicationContext("bean1.xml","bean2.xml");
}
}
获取bean
public class App {
public static void main(String[] args) {
//加载配置文件
//●方式一:类路径加载配置文件
ApplicationContext ctx =new ClassPathXmlApplicationContext("applicationContext.xml");
//●方式二:文件路径加载配置文件
ApplicationContext ctx1 =new FileSystemXmlApplicationContext("F:\\java后端\\project\\spring10_container\\src\\main\\resources\\applicationContext.xml");
//●加载多个配置文件
ApplicationContext ctx2 =new ClassPathXmlApplicationContext("bean1.xml","bean2.xml");
//获取bean的方式
//●方式一:使用bean名称获取
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
//●方式二:使用bean名称获取并指定类型
BookDao bookDao1 =ctx.getBean("bookDao",BookDao.class);
//●方式三:使用bean类型获取
BookDao bookDao2 = ctx.getBean(BookDao.class);
}
}
容器总结
bean相关
<bean id="bookDao" bean的Id
name="dao bookDaoImpl daoImpl" bean别名
class="com.itheima.dao.impl.BookDaoImpl" bean类型,静态工厂类,FactoryBean类
scope="singleton" 控制bean的实例数量
init-method="init" 生命周期初始化方法
destroy-method="destory" 生命周期销毁方法
autowire="byType" 自动装配类型
factory-method="getInstance" bean工厂方法,应用于静态工厂或实例工厂
factory-bean="com.itheima.factory.BookDaoFactory" 实例工厂bean
lazy-init="true" 控制bean延迟加载
/>
依赖注入相关
<bean id="bookService" class="com.itheima.service.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"> setter注入集合类型
<list> list集合
<value>itcast</value> 集合注入简单类型
<ref bean="dataSource"/> 集合注入引用类型
</list>
</property>
</bean>
注解开发定义bean
使用@Component定义bean
@Component("bookDao")
public class BookDaoImpl implements BookDao{
}
@Component
public class BookServiceImpl implements BookService{
}
核心配置文件中通过组件扫描加载bean
<context:component-sdan base-package="com.itheima"/>
Spring提供@Component注解的三个衍生注解
■@Controller:用于表现层bean定义
■@Service:用于业务层bean定义
■@Repository:用于数据层bean定义
@Repository("bookDao")
public class BookDaoImpl implements BookDao
}
@Service
public class BookServiceImpl implements BookService{
}
@Controller
public class BookController{
}
纯注解开发
●Spring3.o开启了纯注解开发模式,使用]ava类替代配置文件,开启了Spring快速开发赛道
●Java类代替Spring核心配置文件
@Configuration
@ComponentScan("com.chj")
public class SpringConfig {
}
●@Configuration注解用于设定当前类为配置类
●@ComponentScani注解用于设定扫描路径,此注解只能添加一次,多个数据请用数组格式
@ComponentScan({com.chj.service","com.chj.dao"})
●Spring3.开启了纯注解开发模式,使用]ava类替代配置文件,开启了Spring快速开发赛道
●读取Spring核心配置文件初始化容器对象切换为读取]ava配置类初始化容器对象
/加载配置文件初始化容器
ApplicationContext ctx =new ClassPathXmlApplicationContext("applicationContext.xml");
//加截配置类初始化容器
ApplicationContext ctx =new AnnotationConfigApplicationContext(SpringConfig.class);
bean作用范围
●使用@Scope定义bean作用范围
@Repository
@Scope("singleton")//prototype
public class BookDaoImpl implements BookDao
bean生命周期
●使用@PostConstruct、@PreDestroy定义bean生命周期
@Repository("bookDao")
@Scope()
public class BookDaoImpl implements BookDao {
@Override
public void save() {
System.out.println("bookDao save.......");
}
@PostConstruct
public void init() {
System.out.println("bookDao init.......");
}
@PreDestroy
public void destroy() {
System.out.println("bookDao destroy.......");
}
}
依赖注入
使用@Autowired注解开启自动装配模式(按类型)
@Service
public class BookServiceImpl implements BookService {
@Autowired
private BookDao bookDao;
@Override
public void save() {
System.out.println("bookService run ........");
bookDao.save();
}
}
●注意:自动装配基于反射设计创建对象并暴力反射对应属性为私有属性初始化数据,因此无需提供setter方法
●注意:自动装配建议使用无参构造方法创建对象(默认),如果不提供对应构造方法,请提供唯一的构造方法
使用@Qualifier注解开启指定名称装配bean
@Service
public class BookServiceImpl implements BookService {
@Autowired
@Qualifier("bookDao")
private BookDao bookDao;
@Override
public void save() {
System.out.println("bookService run ........");
bookDao.save();
}
}
●注意:@Qualifier注解无法单独使用,必须配合@Autowired注解使用
使用@Value实现简单类型注入
@Repository("bookDao")
public class BookDaoImpl implements BookDao{
@Va1ue("100")
private String connectionNum;}
加载properties文件
使用@PropertySource注解加载properties文件
@Configuration
@PropertySource("jdbc.properties")
public class SpringConfig {
}
●注意:路径仅支持单一文件配置,多文件请使用数组格式配置,不允许使用通配符*
第三方bean管理
●使用@Bean配置第三方bean
@Configuration
public class SpringConfig{
@Bean
public DataSource dataSource(){
DruidDataSource ds = new DruidDataSource();
ds.setDriverclassName("com.mysql.cj.jdbc.Driver");
ds.seturl("jdbc:mysq1://localhost:3306/spring_db");
ds.setUsername("root");
ds.setPassword("root");
return ds;
}
● 将独立的配置类加入核心配置
● 方式一:导入式
● 使用@Import注解手动加入配置类到核心配置,此注解只能添加一次,多个数据请用数组格式
@Configuration
@PropertySource("jdbc.properties")
@Import(JdbcConfig.class)
public class SpringConfig {
}
●方式二:扫描式
● 在第三方配置类上添加@Configuration在核心配置类上使用@ComponentScani注解扫描配置类所在的包,加载对应的配置类信息
@Configuration
@ComponentScan({"com.itheima.config","com.itheima.service","com.itheima.dao"})
public class SpringConfig{}
第三方bean依赖注入
●简单类型依赖注入
public class JdbcConfig{
@Value("com.mysql.jdbc.Driver")
private String driver;
@Value("jdbc:mysql://localhost:3306/spring_db")
private String url;
@Value("root")
private String userName;
@Value("root")
private String password;
@Bean
public DataSource dataSource(){
DruidDataSource ds new DruidDataSource();
ds.setDriverclassName(driver);
ds.setUrl(url);
ds.setUsername(userName);
ds.setPassword(userName);
return ds;}
}
●引用类型依赖注入
@Bean
public DataSource dataSource(BookServicebookService){
System.out.println(bookService);
DruidDataSource ds new DruidDataSource();
//属性设置
return ds;}
●引用类型注入只需要为bean定义方法设置形参即可,容器会根据类型自动装配对象
功能 | XML配置 | 注解 |
---|---|---|
定义bean | bean标签 ● id属性 ● class属性 | @Component @Controller @Service @Repository @Componentscan |
设置依赖注入 | setter注入(set方法) ●引用/简单 构造器注入(构造方法) ●引用/简单 自动装配 | @Autowired ●@Qualifier @Value |
配置第三方bean | bean标签 静态工厂、实例工厂、FactoryBean | @Bean |
作用范围 | ●scope属性 | @Scope |
生命周期 | 标准接口 ●init-method ●destroy-method | @PostConstructor @PreDestroy |
Spring整合MyBatis
●MyBatis程序核心对象分析
App.Class
//1.创建SqLSessionFactoryBuilder对象
SqlSessionFactoryBuilder sqlSessionFactoryBuilder new SqlSessionFactoryBuilder();
//2.加qLMapConfig.xmL配置文件
InputStream inputStream Resources.getResourceAsStream("SqlMapConfig.xml");
//3.建SqLSessionFactory.对象
SqlSessionFactory sqlSessionFactory sqlSessionFactoryBuilder.build(inputStream);
//4.获SqLSession
SqlSession sqlSession sqlSessionFactory.openSession();
//5.执行SqLSession.对象执行查询,获取结User
AccountDao accountDao sqlSession.getMapper(AccountDao.class);
Account ac accountDao.findById(2);
System.out.println(ac);
//6.释放资源
sqlSession.close();
SqlMapConfig.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<properties resource="jdbc.properties"/> #初始化属性数据
<typeAliases>
<package name="com.chj.domain"/> #初始化类型别名
</typeAliases>
<environments default="mysql"> #初始化DataSource
<environment id="mysql">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.DriverClassName}"/>
<property name="url" value="${jdbc.Url}"/>
<property name="username" value="${jdbc.Username}"/>
<property name="password" value="${jdbc.Password}"/>
</dataSource>
</environment>
</environments>
<mappers> #初始化映射配置
<package name="com.chj.dao"/>
</mappers>
</configuration>
jdbc.properties
jdbc.DriverClassName=com.mysql.cj.jdbc.Driver
jdbc.Url=jdbc:mysql://localhost:3306/chj?serverTimezone=UTC
jdbc.Username=root
jdbc.Password=root
去xml文件整合mybatis
●数据源
public class JdbcConfig {
@Value("${jdbc.DriverClassName}")
private String driverClassName;
@Value("${jdbc.Url}")
private String url;
@Value("${jdbc.Username}")
private String username;
@Value("${jdbc.Password}")
private String password;
@Bean("dataSource")
public DataSource getDataSource(){
DruidDataSource dataSource=new DruidDataSource();
dataSource.setDriverClassName(driverClassName);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
return dataSource;
}
}
●配置SqlSessionFactoryBean和映射mapper
public class MybatisConfig {
@Bean
public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource){
SqlSessionFactoryBean sqlSessionFactoryBean=new SqlSessionFactoryBean();
sqlSessionFactoryBean.setTypeAliasesPackage("com.chj.domain");
sqlSessionFactoryBean.setDataSource(dataSource);
return sqlSessionFactoryBean;
}
@Bean
public MapperScannerConfigurer mapperScannerConfigurer(){
MapperScannerConfigurer msc=new MapperScannerConfigurer();
msc.setBasePackage("com.chj.dao");
return msc;
}
}
●核心配置类
@Configuration
@ComponentScan("com.chj")
@PropertySource("classpath:jdbc.properties")
@Import({JdbcConfig.class,MybatisConfig.class})
public class SpringConfig {
}
●main方法
public class App1 {
public static void main(String[] args) {
ApplicationContext ctx=new AnnotationConfigApplicationContext(SpringConfig.class);
AccountService accountService = ctx.getBean(AccountService.class);
List<Account> all = accountService.findAll();
System.out.println(all);
}
}
整合JUnit
使用Spring整合]unit专用的类加载器
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes SpringConfig.class)
public class BookServiceTest{
@Autowired
private BookService bookService;
@Test
public void testsave(){
bookService.save();}}
Aop
AOP简介
●AOP(Aspect Oriented Programming)面向切面编程,一种编程范式,指导开发者如何组织程序结构
■ooP(Object Oriented Programming)面向对象编程
●作用:在不惊动原始设计的基础上为其进行功能增强
●Spring理念:无入侵式/无侵入式
AOP核心概念
●连接点(joinPoint):程序执行过程中的任意位置,粒度为执行方法、抛出异常、设置变量等
■在SpringAoP中,理解为方法的执行
●切入点(Pointcut):匹配连接点的式子
■在SpringAOP中,一个切入点可以只描述一个具体方法,也可以匹配多个方法
◆一个具体方法:com.itheima.dao包下的BookDao接口中的无形参无返回值的save方法
◆匹配多个方法:所有的save方法,所有的get开头的方法,所有以Dao结尾的接口中的任意方法,所有带有一个参数的方法
●通知(Advice):在切入点处执行的操作,也就是共性功能
■在SpringAOP中,功能最终以方法的形式呈现
●通知类:定义通知的类
●切面(Aspect):描述通知与切入点的对应关系
AOP入门案例思路分析
案例设定:测定接口执行效率
简化设定:在接口执行前输出当前系统时间
开发模式:XML or 注解
思路分析:
1.导入坐标(pom.xm1)
2.制作连接点方法(原始操作,Dao接口与实现类)
3.制作共性功能
步骤AOP入门案例(注解版)
①:导入aop相关坐标
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
说明:spring-context坐标依赖spring-aop坐标
②:定义dao接口与实现类
public interface BookDao{
public void save();
public void update();}
@Repository
public class BookDaoImpl implements BookDao{
public void save(){
System.out.println(System.currentTimeMiLlis());
System.out.println("book dao save ...")
public void update(){
System.out.println("book dao update...");}}
③:定义通知类,制作通知
public class MyAdvice{
public void before(){
System.out.println(System.currentTimeMillis());
}
④:定义切入点
public class MyAdvice{
@Pointcut("execution(void com.itheima.dao.BookDao.update())")
private void pt(){}
}
说明:切入点定义依托一个不具有实际意义的方法进行,即无参数,无返回值,方法体无实际逻辑
⑤:绑定切入点与通知关系,并指定通知添加到原始连接点的具体执行位置
public class MyAdvice{
@Pointcut("execution(void com.chj.dao.BookDao.update())")
private void pt(){}
@Before("pt()")
public void before(){
System.out.println(System.currentTimeMiLlis());
}
}
⑥:定义通知类受Spring容器管理,并定义当前类为切面类
@Component
@Aspect
public class AopConfig {
@Pointcut("execution(void com.chj.dao.BookDao.delete())")
private void pt(){}
@Before("pt()")
public void method(){
System.out.println(System.currentTimeMillis());
}
@After("pt()")
public void method1(){
System.out.println("after ....");
}
}
⑦:开启Spring对AOP注解驱动支持
@Configuration
@ComponentScan("com.chj")
@EnableAspectJAutoProxy
public class SpringConfig {
}
AOP工作流程
1.Spring容器启动
2.读取所有切面配置中的切入点
@Component
@Aspect
public class AopConfig {
@Pointcut("execution(void com.chj.dao.BookDao.delete())")
private void pt(){}
@Before("pt()")
public void method(){
System.out.println(System.currentTimeMillis());
}
@After("pt()")
public void method1(){
System.out.println("after ....");
}
}
3.初始化bean,判定bean对应的类中的方法是否匹配到任意切入点
●匹配失败,创建对象
●匹配成功,创建原始对象(目标对象)的代理对象
4.获取bean执行方法
●获取bean,调用方法并执行,完成操作
●获取的bean是代理对象时,根据代理对象的运行模式运行原始方法与增强的内容,完成操作
AOP核心概念
●目标对象(Target):原始功能去掉共性功能对应的类产生的对象,这种对象是无法直接完成最终工作的
●代理(Poxy):目标对象无法直接完成工作,需要对其进行功能回填,通过原始对象的代理对象实现
AOP切入点表达式
●切入点:要进行增强的方法
●切入点表达式:要进行增强的方法的描述方式
描述方式一:执行com,itheima.dao包下的BookDao接口中的无参数update方法
execution(void com.itheima.dao.BookDao.update())
描述方式二:执行com.itheima.dao.impl包下的BookDaoImpli类中的无参数update方法
execution(void com.itheima.dao.impl.BookDaoImpl.update())
●切入点表达式标准格式:动作关键字(访问修饰符返回值包名.类/接口名.方法名(参数)异常名)
execution (public User com.itheima.service.UserService.findById (int))
■动作关键字:描述切入点的行为动作,例如execution表示执行到指定切入点
■访问修饰符:public,private等,可以省略
■返回值
■包名
■类/接口名
■方法名
■参数
■异常名:方法定义中抛出指定异常,可以省略
●可以使用通配符描述切入点,快速描述
■ *:单个独立的任意符号,可以独立出现,也可以作为前缀或者后缀的匹配符出现
execution (public com.itheima.*.UserService.find*(*)
匹配com..itheimat包下的任意包中的JserService类或接口中所有find开头的带有一个参数的方法
■ ..:多个连续的任意符号,可以独立出现,常用于简化包名与参数的书写
execution (public User com..UserService.findById (..)
匹配com包下的任意包中的UserService类或接口中所有名称为find Byld的方法
■ +:专用于匹配子类类型
execution(**..*Service+.*(..))
●书写技巧
■所有代码按照标准规范开发,否则以下技巧全部失效
■描述切入点通常描述接口,而不描述实现类
■访问控制修饰符针对接口开发均采用oublic描述(可省略访问控制修饰符描述)】
■返回值类型对于增删改类使用精准类型加速匹配,对于查询类使用*通配快速描述
■包名书写尽量不使用匹配,效率过低,常用*做单个包描述匹配,或精准匹配
■接口名/类名书写名称与模块相关的采用匹配,例如UserService书写成*Service,绑定业务层接口名
■方法名书写以动词进行精准匹配,名词采用*匹配,例如getByld书写成getBy*,selectAll-书写成selectAll
■参数规则较为复杂,根据业务方法灵活调整
■通常不使用异常作为匹配规则
AOP通知类型
●AOP通知描述了抽取的共性功能,根据共性功能抽取的位置不同,最终运行代码时要将其加入到合理的位置
●AOP通知共分为5种类型
■前置通知
●名称:@Before
●类型:方法注解
●位置:通知方法定义上方
●作用:设置当前通知方法与切入点之间的绑定关系,当前通知方法在原始切入点方法前运行
●范例:
@Before("pt()")
public void before(){
System.out.println("before advice...");}
●相关属性:vlue(默认):切入点方法名,格式为类名.方法名0
■后置通知
●名称:@After
●类型:方法注解
●位置:通知方法定义上方
●作用:设置当前通知方法与切入点之间的绑定关系,当前通知方法在原始切入点方法后运行
●范例:
@After("pt()")
public void after(){
System.out.println("after advice ...")
●相关属性:value(默认):切入点方法名,格式为类名.方法名()
■环绕通知(重点)
●名称:@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;}
●@Around注意事项
1.环绕通知必须依赖形参ProceedingJoinPoint才能实现对原始方法的调用,进而实现原始方法调用前后同时添加通知
2.通知中如果未使用ProceedingJoinPoint对原始方法进行调用将跳过原始方法的执行
3.对原始方法的调用可以不接收返回值,通知方法设置成vod即可,如果接收返回值,必须设定为Objecta类型
4.原始方法的返回值如果是void类型,通知方法的返回值类型可以设置成void,也可以设置成Object
5.由于无法预知原始方法运行后是否会抛出异常,因此环绕通知方法必须抛出Throwable对象
■返回后通知(了解)
●名称:@AfterReturning(了解)
●类型:方法注解
●位置:通知方法定义上方
●作用:设置当前通知方法与切入点之间的绑定关系,当前通知方法在原始切入点方法正常执行完毕后运行
●范例:
@AfterReturning("pt()")
public void afterReturning(){
System.out.println("afterReturning advice ...")}
●相关属性:value(默认):切入点方法名,格式为类名.方法名0
■抛出异常后通知(了解)
●名称:@AfterThrowing(了解)
●类型:方法注解
●位置:通知方法定义上方
●作用:设置当前通知方法与切入点之间的绑定关系,当前通知方法在原始切入点方法运行抛出异常后执行
●范例:
@AfterThrowing("pt()")
public void afterThrowing(){
System.out.println("afterThrowing advice ...")
●相关属性:value(默认):切入点方法名,格式为类名.方法名()
AOP通知获取数据
●获取切入点方法的参数
■JoinPoint:适用于前置、后置、返回后、抛出异常后通知
■Proceed]ointPoint:适用于环绕通知
●获取切入点方法返回值
■返回后通知
■环绕通知
●获取切入点方法运行异常信息
■抛出异常后通知
■环绕通知
●oinPoint对象描述了连接点方法的运行状态,可以获取到原始方法的调用参数
@Before("pt()")
public void before(JoinPoint jp){
object[]args jp.getArgs();
System.out.println(Arrays.toString(args));
●Proceed]ointPoint是]oinPoint的子类
@Around("pt()")
public Object around(ProceedingJoinPoint pjp)throws 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("afterReturning 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 advice ..."t);}
●抛出异常后通知可以获取切入点方法运行的异常信息,使用形参可以接收运行时抛出的异常对象
@Around("pt()")
public Object around(ProceedingJoinPoint pjp){
Object ret =null;
try{
ret =pjp.proceed();}
catch (Throwable t){
t.printStackTrace();}
return ret;}
AOP总结
●概念: AOP(Aspect Oriented Programming)面向切面编程,一种编程范式
●作用: 在不惊动原始设计的基础上为方法进行功能增强
●核心概念: 代理(Proxy):SpringAOP的核心本质是采用代理模式实现的
■连接点(JoinPoint):在SpringAOP中,理解为任意方法的执行
■切入点(Pointcut):匹配连接点的式子,也是具有共性功能的方法描述
■通知(Advice):若干个方法的共性功能,在切入点处执行,最终体现为一个方法
■切面(Aspect):描述通知与切入点的对应关系
■目标对象(Target):被代理的原始对象成为目标对象
●切入点表达式标准格式:动作关键字(访问修饰符 返回值 包名.类/接口名.方法名(参数)异常名)
execution(*com.itheima.service.*Service.*(..))
●切入点表达式描述通配符:
■ 作用:用于快速描述,范围描述
■ * : 匹配任意符号(常用)
■ .. : 匹配多个连续的任意符号(常用)
■ + :匹配子类类型
●切入点表达式书写技巧
1.按标准规范开发
2.查询操作的返回值建议使用*匹配
3.减少使用..的形式描述包
4.对接口进行描述,使用*表示模块名,例如UserService的匹配描述为*Service
5.方法名书写保留动词,例如get,使用*表示名词,例如getByIdl匹配描述为getBy*
6.参数根据实际情况灵活调整
●通知类型
■ 前置通知
■ 后置通知
■ 环绕通知(重点)
◆ 环绕通知依赖形参Proceeding.]oinPoint才能实现对原始方法的调用
◆环绕通知可以隔离原始方法的调用执行
◆ 环绕通知返回值设置为Object类型
◆ 环绕通知中可以对原始方法调用过程中出现的异常进行处理
■ 返回后通知
■ 抛出异常后通知
●获取切入点方法的参数
■JoinPoint:适用于前置、后置、返回后、抛出异常后通知,设置为方法的第一个形参
■Proceed]ointPoint:适用于环绕通知
●获取切入点方法返回值
■返回后通知
■环绕通知
●获取切入点方法运行异常信息
■抛出异常后通知
■环绕通知
Spring事务
Spring事务简介
●事务作用:在数据层保障一系列的数据库操作同成功同失败
●Spring事务作用:在数据层或业务层保障一系列的数据库操作同成功同失败
public interface PlatformTransactionManager{
void commit(TransactionStatus status)throws TransactionException;
void rollback(TransactionStatus status)throws TransactionException;}
public class DataSourceTransactionManager{
....
}
使用事务
①:在业务层接口上添加Spring事务管理
public interface AccountService{
@Transactional
public void transfer(String out,String in Double money);}
注意事项
Springi注解式事务通常添加在业务层接口中而不会添加到业务层实现类中,降低耦合
注解式事务可以添加到业务方法上表示当前方法开启事务,也可以添加到接口上表示当前接口所有方法开启事务
②:设置事务管理器
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource){
DataSourceTransactionManager ptm new DataSourceTransactionManager();
ptm.setDataSource(dataSource);
return ptm;}
注意事项
事务管理器要根据实现技术进行选择
MyBatisi框架使用的是JDBC事务
③:开启注解式事务驱动
@Configuration
@ComponentScan("com.itheima")
@PropertySource("classpath:jdbc.properties")
@Import({JdbcConfig.class,MybatisConfig.class})
@EnableTransactionManagement//开启注解事务驱动
public class SpringConfig{
}
Spring事务角色
●事务角色
■事务管理员:发起事务方,在Spring中通常指代业务层开启事务的方法
■事务协调员:加入事务方,在Spring中通常指代数据层方法,也可以是业务层方法
事务相关配置
属性 | 作用 | 示例 |
---|---|---|
readOnly | 设置是否为只读事务 | readOnly=true只读事务 |
timeout | 设置事务超时时间 | timeout=-1(永不超时) |
rollbackFor | 设置事务回滚异常(class) | rollbackFor= {NullPointException.class} |
rollbackForClassName | 设置事务回滚异常(String) | 同上格式为字符串 |
noRollbackFor | 设置事务不回滚异常(class) | noRollbackFor= {NullPointException.class} |
noRollbackForclassName | 设置事务不回滚异常(String) | 同上格式为字符串 |
propagation | 设置事务传播行为 | ........... |
●事务传播行为
SpringMVC
SpringMVC概述
●SpringMVC:是一种基于Java实现MvC模型的轻量级Web框架
●优点
■使用简单,开发便捷(相比于Servlet)
■灵活性强
小结:1.SpringMVC是一种表现层框架技术
2.SpringMVC用于进行表现层功能开发
SpringMVC入门案例
①:使用SpringMVC技术需要先导入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 "{info':'springmvc'}";}
}
③:初始化SpringMVC环境(同Spring环境),设定SpringMVC加载对应的bean
@Configuration
@ComponentScan("com.chj.controller")
public class SpringMvcConfig{}
④:初始化Servlet:容器,加载SpringMVC环境,并设置SpringMVC技术处理的请求
public class ServletContainersInitConfig extends AbstractDispatcherServletInitializer{
protected WebApplicationContext createServletApplicationcontext(){
AnnotationConfigWebApplicationContext ctx new AnnotationConfigWebApplicationContext();
ctx.register(SpringMvcConfig.class);
return ctx;
}
protected String[] getservletMappings(){
return new String[]{"/"};}
protected WebApplicationContext createRootApplicationContext(){
return null; }
}
Controller加载控制与业务bean加载控制
●SpringMVC相关bean(表现层bean)
●Spring控制的bean
●业务bean(Service)】
●功能bean(DataSource等)
●SpringMVC相关bean加载控制
●SpringMVC加载的bean对应的包均在com.chj.controller包内
●Spring相关bean加载控制
●方式一:Spring加载的bean设定扫描范围为com.chj,排除掉controllert包内的bean
●方式二:Spring加载的bean设定扫描范围为精准范围,例如service包、dao包等
●名称:@ComponentScan
●类型:类注解
●范例:
@Configuration
@ComponentScan(value= "com.chj",
excludeFilters @ComponentScan.Filter(
type=FilterType.ANNOTATION,
classes=Controller.class)
)
public class SpringConfig{}
●属性
●excludeFilters:排除扫描路径中加载的bean,需要指定类别(type)与具体项(classes)
●includeFilters:加载指定的bean,需要指定类别(type)与具体项(classes)
●bean的加载格式
public class ServletContainerInitConfig extends AbstractDispatcherServletInitializer {
//加载springMVC容器配置
@Override
protected WebApplicationContext createServletApplicationContext() {
AnnotationConfigWebApplicationContext app=new AnnotationConfigWebApplicationContext();
app.register(SpringMVCConfig.class);
return app;
}
//设置哪些请求归属springMVC处理
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
//加spring容器配置
@Override
protected WebApplicationContext createRootApplicationContext() {
AnnotationConfigWebApplicationContext app=new AnnotationConfigWebApplicationContext();
app.register(SpringConfig.class);
return app;
}
}
请求与响应
Get请求传参
●普通参数:url地址传参,地址参数名与形参变量名相同,定义形参即可接收参数
@RequestMapping("/commonParam")
@ResponseBody
public String commonParam(String name,int age){
System..out,println("普通参数传递name=>"+name);
System.out.println("普通参数传递age=>"+age);
return "{'module':'common param'}";}
Post请求参数
●普通参数:form表单post请求传参,表单参数名与形参变量名相同,定义形参即可接收参数
Post请求中文乱码处理
●为web容器添加过滤器并指定字符集,Spring-web包中提供了专用的字符过滤器
public class ServletContainersInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer{
//配字符编码过滤器
protected Filter[]getServletFilters(){
CharacterEncodingFilter filter new CharacterEncodingFilter();
filter.setEncoding("utf-8");
return new Filter[]{filter};}
}
请求参数(传递json数据)
●名称:@EnableWebMvc
●类型:配置类注解
●位置:SpringMVC配置类定义上方
●作用:开启SpringMVC多项辅助功能
●范例:
@Configuration
@ComponentScan("com.chj.controller")
@EnableWebMvc
public class SpringMvcConfig{}
●名称:@RequestBody
●类型:形参注解
●位置:SpringMVC控制器方法形参定义前面
●作用:将请求中请求体所包含的数据传递给请求参数,此注解一个处理器方法只能使用一次
●范例:
@RequestMapping("/listParamForJson")
@ResponseBody
public String listParamForJson(@RequestBody List<String>likes){
System.out.print1n("1 ist common(json)参数传递list=>"+likes);
return "{'module':'list common for json param'}";}
@RequestBody与@RequestParaml区别
●区别
●@RequestParam用于接收url地址传参,表单传参【application/X-www-form-urlencoded】
●@RequestBody用于接收json数据【application/json】
●应用
●后期开发中,发送json格式数据为主,@RequestBody应用较广
●如果发送非json格式数据,选用@RequestParam接收请求参数
日期类型参数传递
●日期类型数据基于系统不同格式也不尽相同
■2088-08-18
■2088/08/18
■08/18/2088
●接收形参时,根据不同的日期格式设置不同的接收方式
@RequestMapping("/dataParam")
@ResponseBody
public String dataParam(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/M/ddHH:mm:ss)==>"+date2);
return "{'module':'data param'}";}
http://localhost/dataParam?date=2088/08/08&date1=2088-08-18&date2=2088/08/288:08:08
●名称:@DateTimeFormat
●类型:形参注解
●位置:SpringMVC控制器方法形参前面
●作用:设定日期时间型数据格式
●范例:
@RequestMapping("/dataParam")
@ResponseBody
public String dataParam(Date date){
System.out.println("参数传递date=>"+date);
return "{module':'data param'}";}
●属性:pattern:日期时间格式字符串
类型转换器
Converter接口
public interface Converter<S,T>{
@Nullable
T convert(S var1);}
■请求参数年龄数据(String-→Integer) ■日期格式转换(String→Date)
@EnableWebMvc功能之一:根据类型匹配对应的类型转换器
响应
●名称:@ResponseBody
●类型:方法注解
位置:SpringMVC控制器方法定义上方
●作用:设置当前控制器返回值作为响应体
●范例:
@RequestMapping("/save")
@ResponseBody
public String save(){
System.out.println("save...");
return "{'info''springmvc'}";}
REST风格
REST简介
●REST(Representational State Transfer),表现形式状态转换
■传统风格资源描述形式
http://localhost/user/getById?id=1
http://localhost/user/saveUser
■REST风格描述形式
http://localhost/user/1
●优点:
■隐藏资源的访问行为,无法通过地址得知对资源是何种操作
■书写简化
REST风格简介
●按照REST风格访问资源时使用行为动作区分对资源进行了何种操作
■http://localhost/users 查询全部用户信息 GET(查询)
■http://localhost/users/1 查询指定用户信息 GET(查询)
■http://localhost/users 添加用户信息 POST(新增/保存)
■http://localhost/users 修改用户信息 PUT(修改/更新)
■http://localhost/users/1 删除用户信息 DELETE(删除)
●根据REST风格对资源进行访问称为RESTful
注意事项
上述行为是约定方式,约定不是规范,可以打破,所以称REST风格,而不是REST规范
描述模块的名称通常使用复数,也就是加s的格式描述,表示此类资源,而非单个资源,例如:users、books、accounts.…
RESTful快速开发
●名称:@RestController
●类型:类注解
●位置:基于SpringMVC的RESTful开发控制器类定义上方
●作用:设置当前控制器类为RESTful)风格,等同于@Controller.与@ResponseBodyi两个注解组合功能
●范例:
@RestController
public class BookController{}
●名称:@GetMapping @PostMapping @PutMapping @DeleteMapping
●类型:方法注解
●位置:基于SpringMVC的RESTful:开发控制器方法定义上方
●作用:设置当前控制器方法请求访问路径与请求动作,每种对应一个请求动作,例如@GetMapping对应GET请求
●范例:
@GetMapping("/{id}")
public String getById(@PathVariable Integer id){
System.out.println("book getById..."+id);
return "{'module':'book getById'}";}
●属性
●value(默认):请求访问路径
异常处理器
●出现异常现象的常见位置与常见诱因如下:
■框架内部抛出的异常:因使用不合规导致
■数据层抛出的异常:因外部服务器故障导致(例如:服务器访问超时)
■业务层抛出的异常:因业务逻辑书写错误导致(例如:遍历业务书写操作,导致索引异常等)
■表现层抛出的异常:因数据收集、校验等规则导致(例如:不匹配的数据类型间导致异常)
■工具类抛出的异常:因工具类书写不严谨不够健壮导致(例如:必要释放的连接长期未释放等)
项目异常处理方案
●项目异常分类
■业务异常(BusinessException)
◆规范的用户行为产生的异常
◆不规范的用户行为操作产生的异常
■系统异常(SystemException)】
◆项目运行过程中可预计且无法避免的异常
■其他异常(Exception)
◆编程人员未预期到的异常
●
项目异常处理方案
■业务异常(BusinessException)
◆发送对应消息传递给用户,提醒规范操作
■系统异常(SystemException)】
◆发送固定消息传递给用户,安抚用户
◆发送特定消息给运维人员,提醒维护
◆记录日志
■其他异常((Exception)
◆发送固定消息传递给用户,安抚用户
◆发送特定消息给编程人员,提醒维护(纳入预期范围内)
◆记录日志
项目异常处理
①:自定义项目系统级异常
public class SystemException extends RuntimeException{
private Integer code;
public SystemException(Integer code,String message){
super(message);
this.codecode;}
public SystemException(Integer code,String message,Throwable cause){
super(message,cause);
this.code code;}
public Integer getCode(){
return code;
}
public void setCode(Integer code){
this.code code;}
}
②:自定义项目业务级异常
public class BusinessException extends RuntimeException{
private Integer code;
public BusinessException (Integer code,String message){
super(message);
this.codecode;}
public BusinessException (Integer code,String message,Throwable cause){
super(message,cause);
this.code code;}
public Integer getCode(){
return code;
}
public void setCode(Integer code){
this.code code;}
}
③:自定义异常编码(持续补充)
public class Code{
public static final Integer SYSTEM_UNKNOW ERROR 50001;
public static final Integer SYSTEM TIMEOUT ERROR 50002;
public static final Integer PROJECT VALIDATE ERROR 60001;
public static final Integer PROJECT_BUSINESS ERROR 60002;}
④:触发自定义异常
@Service
public class BookServiceImpl implements BookService{
@Autowired
private BookDao bookDao;
public Book getById(Integer id){
if(id <0 ){
throw new BusinessException(Code.PROJECT_BUSINESS_ERROR,"请勿进行非法操作!");
}
return bookDao.getById(id);}
}
⑤:拦截并处理异常
@RestControllerAdvice
public class ProjectExceptionAdvice{
@ExceptionHandler(BusinessException.class)
public Result doBusinessException(BusinessException ex){
return new Result(ex.getCode(),null,ex.getMessage());
}
@ExceptionHandler(SystemException.class)
public Result doSystemException(SystemException ex){
//记录日志(错误推栈)
//发送邮件给开发人员
//发送短信给运维人员
return new Result(ex.getCode(),null,ex.getMessage());
}
@ExceptionHandler(Exception.class)
public Result doException(Exception ex){
//记录日志(错误堆栈)
//发送邮件给开发人员
//发送短信给运维人员
return new Result(Code.SYSTEM_UNKNOW_ERROR,null,"系统繁忙,请联系管理员!");
}
}
拦截器
拦截器概念
●拦截器(Interceptor)是一种动态拦截方法调用的机制
●作用:
■在指定的方法调用前后执行预先设定后的的代码
■阻止原始方法的执行
拦截器与过滤器区别
●归属不同: Filter属于Servlet技术,Interceptor属于SpringMVC:技术
●拦截内容不同:Filter对所有访问进行增强,Interceptor仅针对SpringMVC的访问进行增强
1、拦截器是基于java的反射机制的,而过滤器是基于函数回调(职责链)
2、过滤器依赖与servlet容器,而拦截器不依赖与servlet容器
3、拦截器只能对action请求起作用,而过滤器则可以对几乎所有的请求起作用
4、拦截器可以访问action上下文、值栈里的对象,而过滤器不能
5、在action的生命周期中,拦截器可以多次被调用,而过滤器只能在容器初始化时被调用一次
拦截器入门案例
①:声明拦截器的bean,并实现HandlerInterceptor接口(注意:扫描加载bean)
@Component
public class ProjectInterceptor 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
System.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 ProjectInterceptorprojectInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry){
registry.addInterceptor(projectInterceptor).addPathPatterns("/books");}
}
④:使用标准接口WebMvcConfigurer简化开发(注意:侵入式较强)
@Configuration
@ComponentScan("com.itheima.controller")
@EnableWebMvc
public class SpringMvcConfig implements WebMvcConfigurer{
@Autowired
private ProjectInterceptor projectInterceptor;
public void addInterceptors(InterceptorRegistry registry){
registry.addInterceptor(projectInterceptor).addPathPatterns("/books","/books/*");}
}
执行流程
拦截器参数
●前置处理
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler)throws Exception{
System.out.println("preHandle...");
return true;}
●参数
■request:请求对象
■response:响应对象
■handler:被调用的处理器对象,本质上是一个方法对象,对反射技术中的Methodi对象进行了再包装
●返回值
■返回值为false,被拦截的处理器将不执行
●后置处理
public void postHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler,
ModelAndView modelAndView)throws Exception{
System.out.println("postHandle...");}
●参数
■modelAndView:如果处理器执行完成具有返回结果,可以读取到对应数据与页面信息,并进行调整
●完成后处理
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex)throws Exception{
System.out.println("afterCompletion...");}
●参数
■:如果处理器执行过程中出现异常对象,可以针对异常情况进行单独处理
多拦截器执行顺序
●当配置多个拦截器时,形成拦截器链
●拦截器链的运行顺序参照拦截器添加顺序为准
●当拦截器中出现对原始处理器的拦截,后面的拦截器均终止运行
●当拦截器运行中断,仅运行配置在前面的拦截器的afterCompletion操作
分模块开发
①:创建Maven模块
②:书写模块代码
注意事项
分模块开发需要先针对模块功能进行设计,再进行编码。不会先将工程开发完毕,然后进行拆分
③:通过maven指令安装模块到本地仓库(install指令)
注意事项
团队内部开发需要发布模块功能到团队内部可共享的仓库中(私服)
依赖管理
依赖传递
●依赖具有传递性
■直接依赖:在当前项目中通过依赖配置建立的依赖关系
■间接依赖:被资源的资源如果依赖其他资源,当前项目间接依赖其他资源
依赖传递冲突问题
●路径优先:当依赖中出现相同的资源时,层级越深,优先级越低,层级越浅,优先级越高
●声明优先:当资源在相同层级被依赖时,配置顺序靠前的覆盖配置顺序靠后的
●特殊优先:当同级配置了相同资源的不同版本,后配置的覆盖先配置的
可选依赖
●可选依赖指对外隐藏当前所依赖的资源一不透明
<dependency>
<groupId>com.itheima</groupId>
<artifactId>maven 03_pojo</artifactId>
<version>1.0-SNAPSHOT</version>
<!--可选依赖是隐藏当前工程所依赖的资源,隐藏后对应资源将不具有依赖传递性-->
<optional>false</optional>
</dependency>
排除依赖
●排除依赖指主动断开依赖的资源,被排除的资源无需指定版本一不需要
<dependency>
<groupId>com.itheima</groupId>
<artifactId>maven 04 dao</artifactId>
<version>1.0-SNAPSHOT</version>
<!--排除依赖是隐藏当前资源对应的依赖关系-->
<exclusions>
<exclusion>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</exclusion>
<exclusion>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
</exclusion>
</exclusions>
</dependency>
●排除依赖资源仅指定GA即可,无需指定V
继承与聚合
聚合
●聚合:将多个模块组织成一个整体,同时进行项目构建的过程称为聚合
●聚合工程:通常是一个不具有业务功能的“空”工程(有且仅有一个pom文件)
●作用:使用聚合工程可以将多个工程编组,通过对聚合工程进行构建,实现对所包含的模块进行同步构建
■当工程中某个模块发生更新(变更)时,必须保障工程中与已更新模块关联的模块同步更新,此时可以使用聚合工程来解决批量模块同步构建的问题
聚合工程开发
①:创建Maven模块,设置打包类型为pom
<packaging>pom</packaging>
注意事项
每个maven工程都有对应的打包方式,默认为jar,web工程打包方式为war
②:设置当前聚合工程所包含的子模块名称
<modules>
<modale>../maven_ssm</module>
<module>../maven_pojo</module>
<module>../maven_dao</module>
</modules>
注意事项
聚合工程中所包含的模块在进行构建时会根据模块间的依赖关系设置构建顺序,与聚合工程中模块的配置书写位置无关
参与聚合的工程无法向上感知是否参与聚合,只能向下配置哪些模块参与本工程的聚合
继承
●概念:继承描述的是两个工程间的关系,与jva中的继承相似,子工程可以继承父工程中的配置信息,常见于依赖关系的继承
●作用:
■简化配置
■减少版本冲突
属性
属性配置与使用
①:定义属性
<!--定义自定义属性->
<properties>
<spring.version>5.2.10.RELEASE</spring.version>
<junit.version>4.12</junit.version>
</properties>
②:引用属性
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
多环境配置与应用
多环境开发
①:定义多环境
<!--定义多环境-->
<profiles>
<!--定义具体的环境:生产环境-->
<profile>
<!--定义环境对应的唯一名称-->
<id>env_dep</id>
<!--定义环境中专用的属性值-->》
<properties>
<jdbc.url>jdbc:mysq1://127.0.0.1:3306/ssm_db</jdbc.url>
</properties>
<!--设置默认启动-->
<activation>
<activeByDefault>true</activeByDefault>
</activation>
</profile>
<!--定义具体的环境:开发环境-->
<profile>
<id>env_pro</id>
.....
</profile>
</profiles>
②:使用多环境(构建过程)
mvn 指令 -P 环境定义id
范例:
mvn install -P pro_env
私服
Nexus安装与启动
●启动服务器(命令行启动)
nexus.exe /run nexus
●访问服务器(默认端口:8081)
http://localhost:8081
●修改基础配置信息
■安装路径下etc目录中nexus-default.properties.文件保存有nexus基础配置信息,例如默认访问端口
●修改服务器运行配置信息
■安装路径下bin目录中nexus.vmoptions文件保存有nexus.服务器启动对应的配置信息,例如默认占用内存空间
私服资源操作流程分析
私服仓库分类
仓库类别 | 英文名称 | 功能 | 关联操作 |
宿主仓库 | hosted | 保存自主研发+第三方资源 | 上传 |
代理仓库 | proxy | 代理连接中央仓库 | 下载 |
仓库组 | group | 为仓库编组简化下载操作 | 下载 |
本地仓库访问私服权限设置
配置位置(setting.xml文件中)
<servers>
<server>
<id>heima-release</id>
<username>admin</username>
<password>admin</password>
</server>
<server>
<id>heima-snapshots</id>
<username>admin</username>
<password>admin</password>
</server>
</servers>
<mirrors>
<mirror>
<id>nexus-heima</id>
<mirrorof>*</mirrorof>
<url>http://localhost:8081/repository/maven-public/</url>
</mirror>
</mirrors>
工程上传到私服服务器设置
配置位置(工程pom文件中)
<distributionManagement>
<repository>
<id>heima-release</id>
<url>http://localhost:8081/repository/heima-release/</url>
</repository>
<snapshotRepository>
<id>heima-snapshots</id>
<url>http://localhost:8081/repository/heima-snapshots/</url>
</snapshotRepository>
</distributionManagement>
●发布命令
mvn deploy
私服访问中央服务器设置
配置位置(nexus服务器页面设置)