Spring框架概述
Spring生态圈的主要组成
Spring Framework
Spring Framework系统框架
代码书写现状
解决方案
new
产生对象,转换为由外部提供对象IoC ( Inversion of Control )控制反转
new
产生对象转换为由外部提供对象,此过程中对象创建控制权由程序转移到外部,此思想称为控制反转Spring
技术对IoC
思想进行了实现
Spring
提供了一个容器,称为IoC
容器,用来充当IoC
思想中的外部)IoC
容器负责对象的创建、初始化等一系列工作,被创建或被管理的对象在IoC
容器中统称为Bean
DI ( Dependency Injection )依赖注入
bean
与bean
之间的依赖关系的整个过程,称为依赖注入目标:充分解耦
使用IoC
容器管理bean (IoC)
在Ioc
容器内将有依赖关系的bean
进行关系绑定(DI)
最终效果
IoC
容器中获取,并且获取到的bean
已经绑定了所有的依赖关系思路分析:
步骤:
思路分析:
基于IoC管理bean
Service中使用new形式创建的Dao对象是否保留?(否)
Service中需要的Dao对象如何进入到service中?(提供方法)
Service与Dao间的关系如何描述?(配置)
步骤:
setter
方法bean默认为单例,需要手动设置"scope=prototype"来设置非单例bean
适合交给容器进行管理的bean
不适合交给容器进行管理的bean
bean本质上就是对象,创建bean使用构造方法完成
实例化bean的三种方式
构造方法
提供可访问的构造方法
javapublic class BookDaoImpl implements BookDao{
public BookDaoImpl(){
System.out.println("book constructor is running ...");
}
public void save(){
System.out.println("book dao save ...");
}
}
配置
xml<bean
id="bookDao"
class="com.itheima.dao.impl.BookDaoImpl"
/>
无参构造方法如果不存在,将抛出异常BeanCreationException
javapublic class OrderDaoFactory {
public static OrderDao getOrderDao(){
return new OrderDaoImpl();
}
}
配置
xml<bean
id="orderDao"
factory-method="getOrderDao"
class="com.itheima.factory.OrderDaoFactory"
/>
javapublic class UserDaoFactory {
public UserDao getUserDao(){
return new UserDaoImpl();
}
}
配置
xml<bean id="userDaoFactory"-class="com.itheima.factory.UserDaoFactory" />
<!-- 创建多余bean,不合理-->
<bean
id="userDao"
factory-method="getUserDao"
factory-bean="userDaoFactory"
/>
javapublic class UserDaoFactoryBean implements FactoryBean<UserDao>{
public UserDao getObject() throws Exception {
return new UserDaoImpl();
}
public class<?> getobjectType() {
return UserDao.class;
}
}
配置
xml<bean
id="userDao"
class="com.itheima.factory. UserDaoFactoryBean"
/>
生命周期
bean生命周期
bean生命周期控制
提供生命周期控制方法
javapublic class BookDaoImpl implements BookDao {
public void save() {
System.out.println( "book dao save ... " );
}
public void init(){
system.out.print1n( "book init ..." );
}
public void destory(){
system.out.println( "book destory ..." );
}
}
配置生命周期控制方法
xml<bean id="bookDao"class=" com.itheima.dao .impl.BookDaoImpl" init-method="init" destroy-method= "destory" />
javapublic class BookServiceImpl implements BookService,InitializingBean,DisposableBean {
public void save() {
system.out.println( "book service save ..." );
}
public void afterPropertiesSet() throws Exception{
system.out.println( "afterPropertiesset" );
}
public void destroy() throws Exception {
System.out.println( "destroy" );
}
}
初始化容器
使用bean
关闭/销毁容器
bean销毁时机
ConfigurableApplicationcontext
接口close()
操作ConfigurableApplicationContext
接口registerShutdownHook()
操作javapublic class AppForLifeCycle {
public static void main( String[] args ) {
classPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext( "applicationContext.xml" );
ctx.close();
}
}
思考︰向一个类中传递数据的方式有几种?
思考︰依赖注入描述了在容器中建立bean与bean之间依赖关系的过程,如果bean运行需要的是数字或字符串呢?
依赖注入方式
引用类型
javapublic class BookServiceImpl implements BookService{
private BookDao bookDao;
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
}
}
property
标签ref
属性注入引用类型对象xml<bean id="bookService" class=" com.itheima.service.impl.BookServiceImpl">
<property name="bookDao" ref="bookDao" />
</bean>
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl" />
简单类型
javapublic class BookDaoImpl implements BookDao {
private int connectionNumber;
public void setConnectionNumber(int connectionNumber) {
this.connectionNumber = connectionNumber;
}
}
property
标签 value
属性注入简单类型数据xml<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
<property name="connectionNumber" value="10" />
</bean>
引用类型
javapublic class BookServiceImpl implements BookService{
private BookDao bookDao;
public BookServiceImp1( BookDao bookDao) {
this.bookDao = bookDao;
}
}
constructor-arg
标签 ref
属性注入引用类型对象xml<bean id="bookService" class="com.itheima. service.impl.BookServiceImpl">
<constructor-arg name="bookDao" ref="bookDao" />
</bean>
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl" />
简单类型
set
方法javapublic class BookDaoImpl implements BookDao {
private int connectionNumber;
public void setConnectionNumber( int connectionNumber) {
this.connectionNumber = connectionNumber;
}
}
constructor-arg
标签value
属性注入简单类型数据xml<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
<constructor-arg name="connectionNumber" value="10" />
</ bean>
参数适配
constructor-arg
标签type
属性设置按形参类型注入xml<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
<constructor-arg type="int" value="10" />
<constructor-arg type="java.lang.String" value="mysql"/>
</ bean>
constructor-arg
标签index
属性设置按形参位置注入xml<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
<constructor-arg index="e" value="10" />
<constructor-arg index="1" value="mysql" />
</bean>
依赖自动装配
自动装配方式
配置中使用bean
标签 autowire
属性设置自动装配的类型
xml<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl" />
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl" autowire="byType" />
依赖自动装配特征
注入数组对象
xml<property name="array">
<array>
<value>100</value>
<value>200</value>
<value>300</value>
</array>
</property>
注入List对象
xml<property name="list">
<list>
<value>itcast</value>
<value>itheima</value>
<value>boxuegu</value>
</list>
</property>
注入Set对象
xml<property name="set">
<set>
<value>itcast</value>
<value>itheima</value>
<value>boxuegu</value>
</set>
</property>
注入Map对象
xml<property name="map">
<map>
<entry key="country" value="china"/>
<entry key="province" value="henan"/>
<entry key="city" value="kaifeng"/>
</map>
</property>
注入Properties对象
xml<property name="properties">
<props>
<prop key="country" >china</prop>
<prop key="province ">henan</prop>
<prop key="city">kaifeng</prop>
</props>
</property>
java// 1. 加载类路径下的配置文件
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
// 2. 从文件系统加载配置文件
ApplicationContext ctx = new ClassPathXmlApplicationContext("file:D:\\workspace\\spring_quickstart\\src\\main\\resources\\applicationContext.xml");
java// 1. 使用bean名称获取
DataSource dataSource = (DataSource) ctx.getBean("dataSource");
// 2. 使用bean名称获取并指定类型
DataSource dataSource = ctx.getBean("dataSource", DataSource.class);
// 3. 使用bean类型获取
DataSource dataSource = ctx.getBean(DataSource.class);
xml<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.5</version>
</dependency>
xml<!--applicationContext.xml-->
<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/spring_db"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>
xml<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
xml<!--创建一个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/spring_db"/>
<property name="user" value="root"/>
<property name="password" value="123456"/>
<property name="maxPoolSize" value="1000"/>
</bean>
xml<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
</dependency>
使用properties文件
xml<?xml version="1.0" encoding="UTF-8"?>
<!-- 1.引入context命名空间 -->
<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
">
<!--2. 使用context命名空间加载properties文件-->
<context:property-placeholder location="classpath:*.properties" system-properties-mode="NEVER"/>
<!-- NEVER: 不从系统环境变量中获取值 -->
<!--classpath:*.properties *通配符,使用当前目录所有properties文件-->
<!--classpath*:*.properties *通配符,使用当前工程所有properties文件-->
<!--3. 使用${}获取properties文件中的值-->
<bean 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>
</beans>
@Component
注解java@Component("bookDao")
public class BookDaoImpl implements BookDao {
public void save() {
System.out.println("save book");
}
}
context:component:scan
xml<context:component-scan base-package="com.example.spring_quickstart.dao.impl" />
java BookDao bookDao = (BookDao) ctx.getBean("bookDao");
注意
java// 三种不同的注解方式
@Component("bookDao")
@Repository("bookDao")
@Service("bookService")
public class BookDaoImpl implements BookDao {
public void save() {
System.out.println("save book");
}
}
java@Configuration
@ComponentScan({"com.example.spring_quickstart.dao","com.example.spring_quickstart.Service"})
public class SpringConfig {
}
@Configuration
用于设定当前类为配置类@ComponentScan()
用于设定扫描的软件包,此注解只能加一次,多个数据使用数组格式java// 使用
public class app {
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
BookDao bookDao = ctx.getBean(BookDao.class);
System.out.println(bookDao);
}
}
使用注解管理bean
使用@Scope()
定义使用范围
java@Repository()
@Scope("singleton")
public class BookDaoImpl implements BookDao {
}
使用@PostConstruct
和@PreDestroy
定义bean生命周期
java@Repository()
@Scope("singleton")
public class BookDaoImpl implements BookDao {
public void save() {
System.out.println("save book");
}
@PostConstruct
public void init() {
System.out.println("init...");
}
@PreDestroy
public void destroy() {
System.out.println("destroy...");
}
}
@Autowired
注解开启自动装配模式(按类型)java@Component("bookService")
public class BookServiceImpl implements BookService {
@Autowired
private BookDao bookDao;
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
}
public void save() {
bookDao.save();
}
}
注意
setter
方法指定名称装配bean
java@Component("bookService")
public class BookServiceImpl implements BookService {
@Autowired
@Qualifier("bookDao")
private BookDao bookDao;
public void save() {
bookDao.save();
}
}
注意
@Qualifier
无法单独使用,需配合@Autowired
使用使用@Value
可以进行简单类型的注入
java@Repository("bookDao")
@Scope("singleton")
public class BookDaoImpl implements BookDao {
@Value("${name}")
private String label;
public void save() {
System.out.println("save book--" + label);
}
@PostConstruct
public void init() {
System.out.println("init...");
}
@PreDestroy
public void destroy() {
System.out.println("destroy...");
}
}
加载properties文件
@PropertySource
加载单一properties文件java@Configuration
@ComponentScan({"com.example.spring_quickstart.dao","com.example.spring_quickstart.Service"})
@PropertySource("classpath:jdbc.properties")
public class SpringConfig {
}
*
使用@bean
管理第三方bean
java@Configuration
@ComponentScan({"com.example.spring_quickstart.dao","com.example.spring_quickstart.Service"})
@PropertySource("classpath:jdbc.properties")
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("123456");
return ds;
}
}
将独立配置类加入配置
javapublic class JdbcConfig {
@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("123456");
return ds;
}
}
@Import
注解手动加入配置类到核心配置,此注解只能添加一次,多个数据请用数组格式java@Configuration
@Import(JdbcConfig.class)
public class SpringConfig {
}
@ComponentScan
注解扫描配置类所在的包,加载对应的配置类信息java@Configuration
@ComponentScan({"com.example.spring_quickstart.config","com.example.spring_quickstart.dao","com.example.spring_quickstart.Service"})
@PropertySource("classpath:jdbc.properties")
public class SpringConfig {
}
javapublic class JdbcConfig {
@Value("com.mysql.jdbc.Driver")
private String driver;
@Value("jdbc:mysql://localhost:3306/spring_db")
private String url;
@Value("root")
private String usrname;
@Value("123456")
private String password;
@Bean
public DataSource dataSource() {
DruidDataSource ds = new DruidDataSource();
ds.setDriverClassName(driver);
ds.setUrl(url);
ds.setUsername(usrname);
ds.setPassword(password);
return ds;
}
}
java @Bean
public DataSource dataSource(BookDao bookDao) {
System.out.println(bookDao);
DruidDataSource ds = new DruidDataSource();
// 属性设置
return ds;
}
注意
xml<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.5</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.11</version>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>6.0.3</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>3.0.1</version>
</dependency>
java// SpringConfig
@Configuration
@ComponentScan({"com.example.spring_quickstart.dao", "com.example.spring_quickstart.Service"})
@PropertySource("classpath:jdbc.properties")
@Import({JdbcConfig.class, MyBatisConfig.class})
public class SpringConfig {
}
java// jdbcConfig
public class JdbcConfig {
@Value("${jdbc.driver}")
private String diver;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
@Bean
public DataSource dataSource() {
DruidDataSource ds = new DruidDataSource();
ds.setDriverClassName(diver);
ds.setUrl(url);
ds.setUsername(username);
ds.setPassword(password);
return ds;
}
}
java// MyBatisConfig
@Bean
public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource) {
SqlSessionFactoryBean ssfb = new SqlSessionFactoryBean();
ssfb.setTypeAliasesPackage("com.example.spring_quickstart.entity");
ssfb.setDataSource(dataSource);
return ssfb;
}
@Bean
public MapperScannerConfigurer mapperScannerConfigurer() {
MapperScannerConfigurer msc = new MapperScannerConfigurer();
msc.setBasePackage("com.example.spring_quickstart.dao");
return msc;
}
使用Spring整合Junit专用的类加载器
java@Runwith(SpringJUnit4ClassRunne.class)
@ContextConfiguration(classes = SpringConfig.class)
public class BookServiceTest {
@Autowired
private BookService bookService;
@Test
public void testsave(){
bookService.save();
}
}
AOP(Aspect Oriented Programming)面向切面编程,一种编程范式,指导开发者如何组织程序结构
作用︰在不惊动原始设计的基础上为其进行功能增强
Spring理念: 无入侵式/无侵入式
案例设定︰测定接口执行效率
简化设定︰在接口执行前输出当前系统时间
开发模式
or 注解思路分析
java<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
javapublic interface BookDao {
public void save();
public void update();
}
javapublic class BookDaoImpl implements BookDao {
public void save() {
System.out.println(System.currentTimeMillis());
System.out.println("BookDao.save");
}
public void update() {
System.out.println("BookDao.update");
}
}
aop.MyAdvice
类,制作通知javapublic class MyAdvice {
public void method() {
System.out.println(System.currentTimeMillis());
}
}
javapublic class MyAdvice {
@Pointcut("execution(void com.example.spring_quickstart.dao.BookDao.update())")
private void pt(){}
}
javapublic class MyAdvice {
@Pointcut("execution(void com.example.spring_quickstart.dao.BookDao.update())")
private void pt(){}
@Before("pt()")
public void method() {
System.out.println(System.currentTimeMillis());
}
}
java@Component
@Aspect
public class MyAdvice {
@Pointcut("execution(void com.example.spring_quickstart.dao.BookDao.update())")
private void pt(){}
@Before("pt()")
public void method() {
System.out.println(System.currentTimeMillis());
}
}
@EnableAspectJAutoProxy
,开启Spring对AOP注解驱动的支持java@Configuration
@ComponentScan({"com.example.spring_quickstart"})
@EnableAspectJAutoProxy
public class SpringConfig {
}
Spring容器启动
读取所有切面配置中的切入点
初始化bean,判定bean对应的类中的方法是否匹配到任意切入点
获取bean执行方法
目标对象(Target )︰原始功能去掉共性功能对应的类产生的对象,这种对象是无法直接完成最终工作的
代理( Proxy )︰目标对象无法直接完成工作,需要对其进行功能回填,通过原始对象的代理对象实现
SpringAOP的本质:代理模式
切入点:要增强的方法
切入点表达式:要增强的方法的描述方式
描述方法
excution(void com.example.spring_quickstart.dao.impl.BookDao.Update())
excution(void com.example.spring_quickstart.dao.impl.BookDaoImpl.Upadate())
标准格式
动作关键字 (访问修饰符 返回值 包名.类/接口名.方法名(参数)异常名)
javaexecution (public User com.itheima.service.UserService.findById (int))
使用通配符描述切入点
*
单个独立的任意符号,可以独立出现,也可以作为前缀或者后缀的匹配符出现javaexecution (public * com.itheima.*.UserService.find*(*))
匹配com.itheima
包下的任意包中的UserService
类或接口中所有find
开头的带有一个参数的方法
..
:多个连续的任意符号,可以独立出现,常用于简化包名与参数的书写javaexecution (public User com..UserService.findById(..))
匹配com
包下的任意包中的UserService
类或接口中所有名称为findByld
的方法
+
:专用于匹配子类类型javaexecution(* *..*Service+.*(..))
书写技巧
*
通配快速描述..
匹配,效率过低,常用*
做单个包描述匹配,或精准匹配*
匹配,例如UserService
书写成*Service
,绑定业务层接口名*
匹配,例如getByld
书写成getBy*
,selectAll
书写成selectAll
@Before("pt()")
java@Before("pt()")
public void before() {
System.out.println("before");
}
@After("pt()")
java@After("pt()")
public void after() {
System.out.println("after");
}
@Around("pt()")
java@Around("pt()")
public void around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("before");
// 对原始操作的调用
pjp.proceed();
System.out.println("after");
}
@AfterReturning("pt()")
java@AfterReturning("pt()")
public void afterReturning() {
System.out.println("afterReturning");
}
@AfterThrowing("pt()")
java@AfterThrowing("pt()")
public void afterThrowing() {
System.out.println("afterThrowing");
}
@Around
注意事项
java@Around("pt()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("before");
Object ret = pjp.proceed();
System.out.println("after");
return ret;
}
获取参数
java@Before("pt()")
public void before(JoinPoint jp) {
object[] args = jp.getArgs();
System.out.println(Arrays.toString(args));
}
java@Around("pt()")
public 0bject around(Proceeding]oinPoint pjp) throws Throwable {
object[] args = pjp.getArgs();
System.out.println(Arrays.tostring(args));
object ret = pjp.proceed();
return ret;
}
获取返回值
java@AfterReturning(value = "pt()",returning = "ret")
public void afterReturning(String ret) {
System.out.println("afterReturning advice ..."+ret);
}
java@Around("pt()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
object ret = pjp.proceed();
return ret;
}
需求:对百度网盘分享链接输入密码时尾部多输入的空格做兼容处理
分析:
java@Around( "DataAdvice.servicePt()")
public 0bject trimstring(ProceedingJoinPoint pjp) throws Throwable {
object[] args = pjp.getArgs();
//对原始参数的每一个参数进行操作
for (int i = 0; i < args.length; i++) {
//如果是字符串数据
if(args[i].getclass().equals(String.class)){
//取出数据,trim()操作后,更新数据
args[i] = args[i].toString().trim();
}
}
return pjp.proceed(args);
}
javapublic interface PlatformTransactionManager{
void commit(Transactionstatus status) throws TransactionException;
void rollback(TransactionStatus status) throws TransactionException;
}
模拟银行账户间转账业务
需求:实现任意两个账户间转账操作
需求微缩︰A账户减钱,B账户加钱
分析∶
结果分析︰
解决方法
javapublic interface AccountService {
@Transactional
public void transfer(String out,String in,Double money);
}
注意
java// jdbcConfig
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource){
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
transactionManager.setDataSource(dataSource);
return transactionManager;
}
注意
@EnableTransactionManagement
,开启注解式事务驱动事务角色
属性 | 作用 | 示例 |
---|---|---|
readonly | 设置是否为只读事务 | readOnly=true只读事务 |
timeout | 设置事务超时时间 | timeout = -1(永不超时) |
rollbackFor | 设置事务回滚异常(class) | rollbackFor ={ NullPointException.class} |
rollbackForClassName | 设置事务回滚异常(string) | 同上格式为字符串 |
noRollbackFor | 设置事务不回滚异常(class) | noRollbackFor ={NullPointException.class} |
noRollbackForClassName | 设置事务不回滚异常( string) | 同上格式为字符串 |
propagation | 设置事务传播行为 | ..... |
事务传播行为
转账业务追加日志
需求∶实现任意两个账户间转账操作,并对每次转账操作在数据库进行留痕
需求微缩︰A账户减钱,B账户加钱,数据库记录日志
分析:
实现效果预期∶
存在的问题:
实现效果预期改进:
解决方法
REQUIRES_NEW
(需要新事务)javapublic interface LogService {
@Transactional(propagation = Propagation.REQUIRES_NEW)
void log(String out, String in, Double money);
}
xml<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>
java@Controller
public class UserController {
@RequestMapping("/save")
@ResponseBody
public string save(){
System.out.println("user save ..." );
return "{ 'info ' : 'springmvc ' }";
}
}
java@Configuration
@ComponentScan("com.itheima.controller")
public class SpringMvcConfig {
}
javapublic 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;
}ff
}
启动服务器初始化过程
ServletContainersInitConfig
类,初始化web容器createServletApplicationContext
方法,创建了WebApplicationContext
对象SpringMvcConfig
@ComponentScan
加载对应的bean
UserController
,每个@RequestMapping
的名称对应一个具体的方法getServletMappings
方法,定义所有的请求都通过SpringMVC
单次请求过程
发送请求localhost/save
web容器发现所有请求都经过SpringMVC
,将请求交给SpringMVC
处理
解析请求路径/save
由/save
匹配执行对应的方法save()
执行save()
检测到有@ResponseBody
直接将save()
方法的返回值作为响应求体返回给请求方
SpringMVC相关bean(表现层bean)
Spring控制的bean
SpringMVC相关bean加载控制
Spring相关bean加载控制
@ComponentScan
java@Configuration
@ComponentScan(value = "com.itheima",
excludeFilters = @ComponentScan.Filter(
type = FilterType.ANNOTATION,
classes = Controller.class
)
)
public class SpringConfig {
}
属性
excludeFilters:排除扫描路径中加载的bean,需要指定类别(type)与具体项(Ciasses)
includeFilters:加载指定的bean,需要指定类别(type)与具体项(classes)
Servlet简化开发
javapublic class ServletConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[]{SpringConfig.class};
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{SpringMvcConfig.class};
}
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
}
@RequestMapping
java@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/save")
@ResponseBody
public String save() {
System.out.println("save");
return "{'msg':'save'}";
}
}
@RequestParam
绑定参数关系java@Controller
public class UserController {
@RequestMapping("/save")
@ResponseBody
public String save(String name) {
System.out.println(name);
return "{'msg':'save'}";
}
}
POJO参数
嵌套POJO参数
乱码处理,在servlet配置文件中添加过滤器,可以解决POST传参乱码
javaprotected Filter[] getServletFilters() {
CharacterEncodingFilter filter = new CharacterEncodingFilter();
filter.setEncoding("UTF-8");
return new Filter[]{filter};
}
<uriEncoding>UTF-8</uriEncoding>
,解决GET乱码问题xml<plugins>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<port>8081</port>
<path>/</path>
<uriEncoding>UTF-8</uriEncoding>
</configuration>
</plugin>
</plugins>
日期类型参数传递
java@RequestMapping("/dataParam")
@ResponseBody
public String dataParam(@DateTimeFormat(pattern = "yyyy-MM-dd") Date date){
System.out.println("参数传递date ==> "+date);
return "{'module':'data param'}";
}
java@RequestMapping("/toPage")
public String toPage(){
return "page.jsp";
}
@ResponseBody
java@RequestMapping("/toJsonPO30")
@ResponseBody
public User toJsonPOJO(){
User user = new User();
user.setName("赵云");
user.setAge(41);
return user;
}
REST (Representational State Transfer),表现形式状态转换
优点:
按照REST风格访问资源时使用行为动作区分对资源进行了何种操作
GET
(查询)GET
(查询)POST
(新增/保存)PUT
(修改/更新)DELETE
(删除)根据REST风格对资源讲行访问称为RESTful
注意
SSM整合流程
javapublic class Result {
private Object data;
private Integer code;
private String msg;
}
javapublic 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 SAVE_ERR = 20010;
public static final Integer DELETE_ERR = 20020;
public static final Integer UPDATE_ERR = 20030;
public static final Integer GET_ERR = 20040;
}
GET_OK
,GET_ALL_OK
,GET_PAGE_OK
java@RequestMapping("/books")
public class Bookcontroller {
@Autowired
private BookService bookService;
@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);
}
}
出现异常现象的常见位置与常见诱因如下:
异常处理器
java@RestControllerAdvice
public class ProjectExceptionAdvice {
@ExceptionHandler(Exception.class)
public Result doException(Exception ex){
return new Result(500,null);
}
}
项目异常分类
业务异常(BusinessException)
系统异常(SystemException)
其他异常(Exception)
项目异常处理方案
javapublic class SystemException extends RuntimeException
private Integer 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 Integer getcode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
}
javapublic class BusinessException extends RuntimeException{
private Integer 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 Integer getcode( ) {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
}
javapublic class code {
public static final Integer SYSTEM_UNKNOW_ERROR = 50001;
public static final Integer SYSTEM_TIMEOUT_ERROR = 50002;
public static final Integer PR0JECT_VALIDATE_ERROR = 60001;
public static final Integer PROJECT_BUSINESS_ERROR = 60002;
}
java@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);
}
}
java@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,"系统繁忙,请联系管理员!");
}
}
java{
"data": {
"id": 1,
"type": "计算机理论",
"name": "Spring实战第5版",
"description": "Spring入门经典教程,深入理解Spring原理技术内幕"
},
"code" : 20041,
"msg" : null
}
java{
"data" : null,
"code" : 60002,
"msg":"请勿进行非法操作!"
}
拦截器(Interceptor )是一种动态拦截方法调用的机制,在SpringWC中动态拦截控制器方法的执行
作用:
执行流程
java@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...");
}
}
java@Configuration
public class SpringMvcSupport extends WebMvcConfigurationSupport {
@Override
public void addInterceptors(InterceptorRegistry registry) {
...
}
}
java@Configuration
public class SpringMvcSupport extends WebMvcConfigurationSupport {
@Autowired
private ProjectInterceptor projectInterceptor;
@Override
public void addInterceptors( InterceptorRegistry registry) {
registry.addInterceptor(projectInterceptor).addPathPatterns("/books");
}
}
java@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/*");
}
}
javapublic boolean preHandle(HttpServletRequest request,
HttpservletResponse response,
object handler) throws Exception {
System.out.println("preHandle...");
return true;
}
javapublic void postHandle(HttpServletRequest request,
HttpservletResponse response,object handler,
ModelAndView modelAndView) throws Exception {
System.out.println( "postHandle..." );
}
javapublic void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
object handler,
Exception ex) throws Exception {
System.out.println("aftercompletion...");
}
拦截器链的运行顺序
依赖管理
xml<!--设置当前项目所依赖的所有jar-->
<dependencies>
<!--设置具体的依赖-->
<dependency>
<!--依赖所属群组id-->
<groupId>org.springframework</groupId>
<!--依赖所属项目id-->
<artifactId>spring-webmvc</ artifactId>
<!--依赖版本号-->
<version>5.2.10.RELEASE</version>
</dependency>
</ dependencies>
依赖具有传递性
xml<dependency>
<groupId>com.itheima</ groupId>
<artifactId>maven_03_pojo</ artifactId>
<version>1.0-SNAPSHOT</version>
<!--可选依赖是隐藏当前工程所依赖的资源,隐藏后对应资源将不具有依赖传递性-->
<optional>false</optional>
</ dependency>
xml<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>
groupId
artifactId
即可,无需指定vesion
聚合工程开发
pom
xml<packaging>pom</packaging>
xml<modules>
<module>../maven_ssm</module>
<module>../maven_pojo</module>
<module>../maven_dao</module>
</modules>
继承关系
xml<packaging>pom</packaging>
xml<dependencies>
<dependency>
<groupId>org.springframework</ groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
...
</dependencies>
xml<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</ artifactId>
<version>1.1.16</version>
</dependency>
...
</dependencies>
</dependencyManagement>
xml<!--定义该工程的父工程-->
<parent>
<groupId>com.itheima</groupId>
<artifactId>maven_parent</artifactId>
<version>1.0-SNAPSHOT</version>
<!--填写父工程的pom文件-->
<relativePath>../maven_parent/pom.xml</relativePath>
</parent>
xml<!--定义自定义属性-->
<properties>
<spring.version>5.2.10.RELEASE</spring.version>
<junit.version>4.12</junit.version>
</properties>
xml<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</ artifactId>
<version>${spring.version}</version>
</dependency>
xml<!--定义自定义属性-->
<properties>
<spring.version>5.2.10.RELEASE</ spring.version>
<junit.version>4.12</junit.version>
<jdbc.ur1>jdbc:mysql://127.0.0.1:3306/ssm_db</jdbc.url>
</ properties>
xmljdbc.driver=com.mysql.jdbc.Driver jdbc.url=${jdbc.ur1} jdbc.username=root jdbc.password=root
xml<build>
<resources>
<resource>
<directory>${project.basedir}/src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
</ build>
xml<plugin>
<groupId>org.apache.maven.plugins</ groupId>
<artifactId>maven-war-plugin</ artifactId>
<version>3.2.3</version>
<configuration>
<failonMissingwebxml>false</failOnMissingwebXml>
</configuration>
</plugin>
属性分类 | 引用格式 | 示例 |
---|---|---|
自定义属性 | ${自定义属性名} | ${spring.version} |
内置属性 | ${内置属性名} | ${basedir} ${version} |
Setting属性 | $ {setting.属性名} | ${settings.localRepository} |
Java系统属性 | ${系统属性分类.系统属性名} | ${user.home} |
环境变量属性 | $ {env .环境变量属性名} | ${env.JAVA_HOME} |
工程版本:
发布版本
多环境开发
xml<!--定义多环境-->
<profiles>
<!--定义具体的环境:生产环境-->
<profile>
<!--定义环境对应的唯一名称-->
<id>env_dep</id>
<!--定义环境中专用的属性值-->
<properties>
<jdbc.url>jdbc :mysql://127.0.0.1:3306/ssm_db</jdbc.url>
</properties>
<!--设置默认启动-->
<activation>
<activeByDefault>true</activeByDefault>
</activation>
</ profile>
<!--定义具体的环境:开发环境-->
<profile>
<id>env_pro</id>
…
</profile>
</profiles>
bashmvn 指令 -P 环境定义id
# 示例
mvn install -P pro_env
bashmvn 指令 —D skipTests
# 示例
mvn install -D skipTests
SpringBoot是由Pivotal团队提供的全新框架,其设计目的是用来简化Spring应用的初始搭建以及开发过程
Spring程序缺点
SpringBoot程序优点
Spring程序与SpringBoot程序对比
类/配置文件 | Spring | SpringBoot |
---|---|---|
pom文件中的坐标 | 手工添加 | 勾选添加 |
web3.0配置类 | 手工制作 | 无 |
Spring/SpringMVC配置类 | 手工制作 | 无 |
控制器 | 手工制作 | 手工制作 |
注意
bashjava -jar springboot.jar
xml<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
springboot最新版本(已知>2.5.14)存在某些问题,比如使用Autowired
自动填充在运行时会报错,使用低版本即可
SpringBoot提供了多种属性配置格式
propertiesserver.port=80
ymlserver:
port: 81
yamlserver:
port: 82
SpringBoot配置文件加载顺序
注意
YAML (YAML Ain't Markup Language) ,一种数据序列化格式
优点:
YAML文件扩展名
语法规则
#
表示注释核心规则:数据前面要加空格与冒号隔开
数组
yamllikes:
- music
- movies
- books
数据读取
@Value
读取单个数据,属性名引用方式:${一级属性名.二级属性名....}
多环境启动
多环境启动命令格式
bashjava -jar springboot.jar --spring.profiles.active=test
bashjava -jar springboot.jar --server.port=88
bashjava -jar springboot.jar --server.port=88 --spring.profiles.active=test
SpringBoot中4级配置文件
作用:
java@SpringBootTest
class Springboot07JunitApplicationTests {
@Autowired
private BookService bookService;
@Test
public void testSave(){
bookService.save();
}
}
yamlspring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/ssm_db
username: root
password: 123456
type: com.alibaba.druid.pool.DruidDataSource
jdbc:mysql://localhost:3306/ssm_db?serverTimezone=UTC
java@Mapper
public interface UserDao {
@Select("select * from user")
public List<user> getAll();
}
xml<!-- 阿里数据库连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.16</version>
</dependency>
yamlspring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/db1
username: root
password: 123456
案例实现方案分析
xml<!--mybatisplus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.3</version>
</dependency>
<!--druid-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.6</version>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
修改mysql-connector
xml<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.0.32</version>
<scope>runtime</scope>
</dependency>
yamlserver:
port: 8080
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/db1
username: root
password: 123456
javapackage com.demo.vuespringboot_ssmp.pojo;
import lombok.Data;
@Data
public class Book {
public Integer id;
private String name;
private String type;
private String description;
}
mybatisplus
druid
xml<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.3</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.6</version>
</dependency>
java@Data // 使用lombok自动添加getter和setter
@TableName("tb_book") // 使实体类对应数据库的表名
public class Book {
public Integer id;
private String name;
private String type;
private String description;
}
java@Mapper
public interface BookMapper extends BaseMapper<Book> {
// 继承mybatisplus的基础父类,提供一系列内置方法(前提是实体类名与数据库表名相同或者添加了表名注解),也可以自己写方法调用数据库
@Select("select * from tb_book where id = #{id}")
public Book getBookById(Integer id);
}
java@SpringBootTest
public class BookMapperTestCase {
@Autowired
private BookMapper bookMapper;
@Test
void testGetBookById() {
System.out.println(bookMapper.selectById(1));// 使用的是mabatisplus内置的方法
}
}
注意事项
yamlmybatis-plus:
global-config:
db-config:
id-type: auto
yamlmybatis-plus:
global-config:
db-config:
id-type: auto
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
java@Configuration
public class MybatisplusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
return interceptor;
}
}
java@Test
void testGetBookByPage() {
IPage page = new Page(2, 5);
bookMapper.selectPage(page, null);
}
Service层接口定义与数据层接口定义具有较大区别,不要混用
selectByUserNameAndPassword(String username,String password);
login(String username,String password);
创建service接口与实现类
java@Service
public class BookServiceImpl implements BookService {
@Autowired
private BookMapper bookMapper;
@Override
public Boolean save(Book book) {
return bookMapper.insert(book) == 1;
}
@Override
public Boolean update(Book book) {
return bookMapper.updateById(book) == 1;
}
@Override
public Boolean delete(Integer id) {
return bookMapper.deleteById(id) == 1;
}
@Override
public Book getById(Integer id) {
return bookMapper.selectById(id);
}
@Override
public List<Book> getAll() {
return bookMapper.selectList(null);
}
@Override
public IPage<Book> getPage(Integer page, Integer size) {
IPage<Book> ipage = new Page(page, size);
return bookMapper.selectPage(ipage, null);
}
}
创建测试类
java@SpringBootTest
public class BookServiceTestCase {
@Autowired
private BookService bookService;
@Test
void testSave() {
Book book = new Book();
book.setName("SpringBoot");
book.setType("Java");
book.setDescription("SpringBoot是一个java框架");
bookService.save(book);
}
@Test
void testUpdate() {
Book book = new Book();
book.setId(18);
book.setName("Django");
book.setType("Python");
book.setDescription("Django是一个python框架");
bookService.update(book);
}
@Test
void testDelete() {
bookService.delete(16);
}
@Test
void testGetAll() {
bookService.getById(null);
}
@Test
void testGetBookByPage() {
IPage page = bookService.getPage(2, 5);
System.out.println(page.getRecords());
System.out.println(page.getTotal());
}
}
ISerivce<T>
)与业务层通用实现类(ServiceImpl<M,T>
)java// 接口
public interface IBookService extends IService<Book> {
}
// 实现类
@Service
public class IBookServiceImpl extends ServiceImpl<BookMapper, Book> implements IBookService {
}
java@RestController
@RequestMapping("/books")
public class BookController {
@Autowired
private IBookService bookService;
/***
* 根据id查询图书
* @param id
* @return Book
*/
@GetMapping("/{id}")
public Book getById(@PathVariable Integer id) {
return bookService.getById(id);
}
/***
* 查询所有图书
* @return List<Book>
*/
@GetMapping
public List<Book> getAll() {
return bookService.list();
}
/***
* 上传图书
* @param book
* @return Boolean
*/
@PostMapping
public Boolean save(@RequestBody Book book) {
return bookService.save(book);
}
/***
* 更新图书
* @param book
* @return Boolean
*/
@PutMapping
public Boolean update(@RequestBody Book book) {
return bookService.updateById(book);
}
/***
* 删除图书
* @param id
* @return Boolean
*/
@DeleteMapping("/{id}")
public Boolean delete(@PathVariable Integer id) {
return bookService.removeById(id);
}
}
javapublic IPage<Book> getPage(Integer page, Integer size) {
IPage ipage = new Page(page, size);
bookMapper.selectPage(ipage, null);
return ipage;
}
java/***
* 分页查询图书
* @param page
* @param size
* @return IPage<Book>
*/
@GetMapping("/page/{page}/{size}")
public IPage<Book> getBookByPage(@PathVariable Integer page, @PathVariable Integer size) {
IPage ipage = new Page(page, size);
bookService.page(ipage, null);
return ipage;
}
javapublic class Result {
private int code;
private String msg;
private Object data;
public Result(int code, String msg) {
this.code = code;
this.msg = msg;
}
public Result(int code, String msg, Object data) {
this.code = code;
this.msg = msg;
this.data = data;
}
@Override
public String toString() {
return "Result{" +
"code=" + code +
", msg='" + msg + '\'' +
", data=" + data +
'}';
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
}
javapublic class Code {
// 成功
public static final int SAVE_OK = 20011;
public static final int DELETE_OK = 20021;
public static final int UPDATE_OK = 20031;
public static final int GET_OK = 20041;
public static final int LIST_OK = 20051;
public static final int PAGE_OK = 20061;
// 失败
public static final int SAVE_ERR= 20010;
public static final int DELETE_ERR = 20020;
public static final int UPDATE_ERR = 20030;
public static final int GET_ERR = 20040;
public static final int LIST_ERR = 20050;
public static final int PAGE_ERR = 20060;
}
略
json{
"code": 20041,
"msg": "查询成功",
"data": [
{
"id": 46,
"name": "",
"type": "eqweqwe3123",
"description": "eqweq312412"
},
{
"id": 47,
"name": "qweqw342",
"type": "eqweqwe",
"description": "eqweq"
}
]
}
json{
"timestamp": "2023-03-18T11:32:03.856+00:00",
"status": 400,
"error": "Bad Request",
"path": "/books"
}
@RestControllerAdvice
注解,在处理异常方法上加上@ExceptionHandler
注解java@RestControllerAdvice
public class ProjectExceptionAdvice {
@ExceptionHandler
public Result handleException(Exception e) {
// 记录日志
// ...
// 打印异常信息
e.printStackTrace();
return new Result(Code.ERROR, e.getMessage());
}
}
java@GetMapping("/{id}")
public Result getById(@PathVariable Integer id) throws IOException {
Book book = bookService.getById(id);
if (book == null)
throw new IOException("查询失败");
return new Result(Code.GET_OK, "查询成功", book);
}
java/***
* 条件查询
* @param book book
* name 图书名
* type 图书类型
* description 图书描述
* @return List<Book>
*/
@Select("select * from tb_book where name like '%${name}%' and type like '%${type}%' and description like '%${description}%'")
public List<Book> getBook(Book book);
javapublic List<Book> getBook(Book book) throws IOException {
List<Book> books;
try {
books = bookMapper.getBook(book);
} catch (Exception e) {
throw new IOException("查询失败");
}
return books;
}
java/***
* 条件查询
* @param book book
* name 图书名
* type 图书类型
* description 图书描述
* @return Result
*/
@GetMapping("/")
public Result getBook(Book book) throws IOException {
List<Book> books = bookService.getBook(book);
if (!checkBooks(books))
throw new IOException("查询失败");
return new Result(Code.GET_OK, "查询成功", books);
}
bashmvn package
bashjava -jar 项目名称.jar
注意
xml<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
windows
bash# 查询端口
netstat -ano
# 查询指定端口
netstat -ano | findstr 端口号
# 根据进程PID查询进程名称
tasklist | findstr 进程PID
# 根据PID关闭进程
taskkill -f -pid 进程PID号 # 需要管理员权限
# 根据进程名称关闭进程
taskkill -f -t -im 进程名称
在项目运行命令中加入临时属性
bashjava -jar 项目名称 --临时属性
可以在启动sprng boot 程序时断开读取外部临时配置对应的入口
java@SpringBootApplication
public class SsmpApplication {
public static void main(String[] args) {
// 去掉启动时的临时配置入口
SpringApplication.run(SsmpApplication.class);
}
}
日志(( log)作用
java// 创建日志对象
private static final Logger log = (Logger) LoggerFactory.getLogger(BookController.class);
javalog.debug("信息");
log.info("信息");
log.warn("信息");
log.error("信息");
yaml# 显示日志等级
# application.yml
logging:
# 设置分组
group:
ebank: com.demo.springboot.controller, com.demo.springboot.service
iservice: com.alibaba
level:
root: debug
# 设置某个包的日志级别
com.demo.springboot: debug
# 设置分组日志级别
ebank: info
xml<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<build>
<plugins>
<plugin>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
@Slf4j
java@Slf4j
@RestController
@RequestMapping("/books")
public class BookController {
@Autowired
private IBookService bookService;
javalog.debug("信息");
yaml# 设置日志模板格式
pattern:
console: "%d - %m %n" # 1
console: "%d %clr(%5p) %m %n" # 2
yaml# 基本的文件日志配置
pattern:
name: server.log
yaml# 滚动日志
logging:
file:
name: "server.log"
logback:
rollingpolicy:
max-file-size: 4KB
file-name-pattern: n%d{yyyy-MM-dd}.%i.server.log
热部署
添加依赖
xml<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
修改代码
重新构建项目(Ctrl + F9
)
关于热部署
注意
默认不触发重启的目录列表
yaml# 自定义排除项
spring:
devtools:
restart:
exclude: static/**,public/**,config/**
Ctrl+F9
重新构建,JRebel会自动部署.
拼接
javaprivate long time = 1000 * 60 * 60 * 24; // 一天
private String signature = "admin";
// 加密生成token
@Test
public void JwtTest() {
JwtBuilder jwtBuilder = Jwts.builder();
String jwtToken = jwtBuilder // 创建jwtToken
// header
.setHeaderParam("typ", "JWT")
.setHeaderParam("alg", "HS256")
// payload
.claim("userId", 1)
.claim("userName", "admin")
.claim("userType", 1)
.setSubject("admin-test")
.setExpiration(new Date(System.currentTimeMillis() + time)) // 设置有效时间
.setId(UUID.randomUUID().toString())
// signature
.signWith(SignatureAlgorithm.HS256, signature) //签名
.compact();
System.out.println(jwtToken);
}
// 解密
@Test
public void parse() {
String token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VySWQiOjEsInVzZXJOYW1lIjoiYWRtaW4iLCJ1c2VyVHlwZSI6MSwic3ViIjoiYWRtaW4tdGVzdCIsImV4cCI6MTY3OTU2ODY5MiwianRpIjoiMDExMzRjOGUtZjBlMi00YTA0LWFkZjctMTU0NDAxOWM4MTZmIn0.wXSCIH7uPWrP5PTDLCapWx2NrFW2jdslJe2UtWxrHC0";
JwtParser jwtParser = Jwts.parser();
Jws<Claims> claimsJws = jwtParser.setSigningKey(signature).parseClaimsJws(token);
Claims body = claimsJws.getBody();
System.out.println(body.get("userId"));
System.out.println(body.get("userName"));
System.out.println(body.getId());
System.out.println(body.getSubject());
System.out.println(body.getExpiration());
}
xml<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.1</version>
</dependency>
xml<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.16</version>
</dependency>
ymlserver:
port: 80
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/ssm_db
username: root
password: 123456
制作实体类与标结构(类名与表名对应,属性名与字段名对应)
定义数据接口,继承BaseMapper<实体类>
java@Mapper
public interface BookDao extends BaseMapper<Book> {
}
java@SpringBootTest
class SpringbootMybatisplusApplicationTests {
@Autowired
private BookDao bookDao;
@Test
void contextLoads() {
List<Book> books = bookDao.selectList(null);
System.out.println(books);
}
}
MyBatisPlus是基于MyBatis框架基础上开发的增强型工具,旨在简化开发、提高效率
注意最新的springboot版本使用mybatisplus会出现无法创建bean的情况
功能 | 自定义接口 | MyBatisPlus接口 |
---|---|---|
新增 | boolean save(T t) | int insert(T t) |
删除 | boolean delete(int id) | int deleteById(Serialzable id) |
修改 | boolean update(T t) | int updateById(T t) |
根据id查询 | T getById(int id) | T selectById(T t) |
查询全部 | List<T> getAll() | List<T> selectList() |
分页查询 | PageInfo<T> getAll(int page,int size) | IPage<T> selectPage(IPage<T> page) |
按条件查询 | List<T> getAll(Condition condition) | IPage<T> selectPage(Wrapper<T> queryWrapper) |
xml<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
<scope>provided</scope>
</dependency>
@Data
get/set
方法,无参/无参构造方法,toString
方法,hashCode
方法,equals
方法等java@Data
public class User {
private Long id;
private String name;
private String password;
private Integer age;
private String tel;
}
java@Configuration
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mpInterceptor(){
// 1. 定义拦截器
MybatisPlusInterceptor mpInterceptor = new MybatisPlusInterceptor();
// 2. 添加具体拦截器
mpInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
return mpInterceptor;
}
}
javaIPage page = new Page(2,3); // 当前页 每页多少条
userDao.selectPage(page,null);
System.out.println("当前页码:"+page.getCurrent());
System.out.println("每页数据总量:"+page.getsize());
System.out.println("总页数:"+page .getPages());
System.out.println("数据总量:"+page.getTotal());
System.out.println("当前页数据:"+page.getRecords());
yaml# 开启mybatis-plus的日志
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
javaQueryWrapper<User> qw = new QueryWrapper<User>();
//查询年龄大于等于18岁,小于65岁的用户
qw.lt( "age",65);
qw.ge( "age",18);
List<User> userList = userDao.selectList(qw);
System.out.println(userList);
javaQueryWrapper<User> qw = new QueryWrapper<User>();
//查询年龄大于等于18岁,小于65岁的用户
qw.lt("age",65).ge("age",18);
List<User> userList = userDao.selectList(qw);
System.out.println(userList);
javaQueryWrapper<User> qw = new QueryWrapper<User>();
//查询年龄大于等于18岁,小于65岁的用户
qw.lambda().lt(User::getAge,65).ge(User::getAge,18);
List<User> userList = userDao.selectList(qw);
System.out.println(userList);
javaLambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
//查询年龄大于等于18岁,小于65岁的用户
lqw.lt(User::getAge,65).ge(User::getAge,18);
List<User> userList = userDao.selectList(lqw);
System.out.println(userList);
组合查询条件
javaqw.lambda().lt(User::getAge,65).ge(User::getAge,18);
javaqw.lambda().lt(User::getAge,65).or().ge(User::getAge,18);
javaLambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
if(null != userQuery.getAge()){
lqw.ge(User::getAge,userQuery.getAge());
}
if(null != userQuery.getAge2()){
lqw.lt(User::getAge,userQuery.getAge2());
}
List<User> userList = userDao.selectList(lqw);
System.out.println(userList);
javaLambdaQueryWrapper<User> lqw = new LambdaQuerywrapper<User>();
lqw.ge(null != userQuery.getAge(),User::getAge,userQuery.getAge());
lqw.lt(null != userQuery.getAge2(),User::getAge,userQuery.getAge2());
List<User> userList = userDao.selectList(lqw);
System.out.println(userList);
javaLambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
lqw.ge(null != userQuery.getAge(),User::getAge,userQuery.getAge())
.lt(null != userQuery.getAge2(),User::getAge,userQuery.getAge2());
List<User> userList = userDao.selectList(lqw);
System.out.println(userList);
javaLambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
lqw.select(User::getId,User::getName,User::getAge);
List<User> userList = userDao.selectList(lqw);
System.out.println(userList);
javaQueryWrapper<User> qm = new QueryWrapper<User>();
qm.select("count(*) as nums ,gender");
qm.groupBy("gender");
List<Map<String,object>> maps = userDao.selectMaps(qm);
System.out.println(maps);
默认情况下,SpringBoot项目在启动的时候会自动的创建IOC容器(也称为Spring容器),并且在启动的过程当中会自动的将bean对象都创建好,存放在IOC容器当中。应用程序在运行时需要依赖什么bean对象,就直接进行依赖注入就可以了。
而在Spring容器中提供了一些方法,可以主动从IOC容器中获取到bean对象,下面介绍3种常用方式:
根据name获取bean
javaObject getBean(String name)
根据类型获取bean
java<T> T getBean(Class<T> requiredType)
根据name获取bean(带类型转换)
java<T> T getBean(String name, Class<T> requiredType)
在前面我们提到的IOC容器当中,默认bean对象是单例模式(只有一个实例对象)。那么如何设置bean对象为非单例呢?需要设置bean的作用域。
在Spring中支持五种作用域,后三种在web环境才生效:
作用域 | 说明 |
---|---|
singleton | 容器内同名称的bean只有一个实例(单例)(默认) |
prototype | 每次使用该bean时会创建新的实例(非单例) |
request | 每个请求范围内会创建新的实例(web环境中,了解) |
session | 每个会话范围内会创建新的实例(web环境中,了解) |
application | 每个应用范围内会创建新的实例(web环境中,了解) |
知道了bean的5种作用域了,我们要怎么去设置一个bean的作用域呢?
java@Scope("prototype")
@RestController
@RequestMapper("/depts")
public class DetpController {
}
延迟加载
java@Lazy //延迟加载(第一次使用bean对象时,才会创建bean对象并交给ioc容器管理)
之前我们所配置的bean,像controller、service,dao三层体系下编写的类,这些类都是我们在项目当中自己定义的类(自定义类)。当我们要声明这些bean,也非常简单,我们只需要在类上加上@Component以及它的这三个衍生注解(@Controller、@Service、@Repository),就可以来声明这个bean对象了。 但是在我们项目开发当中,还有一种情况就是这个类它不是我们自己编写的,而是我们引入的第三方依赖当中提供的。
在pom.xml文件中,引入dom4j:
xml<!--Dom4j-->
<dependency>
<groupId>org.dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>2.1.3</version>
</dependency>
dom4j就是第三方组织提供的。 dom4j中的SAXReader类就是第三方编写的。
当我们需要使用到SAXReader对象时,直接进行依赖注入是不是就可以了呢?
结论:第三方提供的类是只读的。无法在第三方类上添加@Component注解或衍生注解。
那么我们应该怎样使用并定义第三方的bean呢?
解决方案1:在启动类上添加@Bean标识的方法
java@SpringBootApplication
public class SpringbootWebConfig2Application {
public static void main(String[] args) {
SpringApplication.run(SpringbootWebConfig2Application.class, args);
}
//声明第三方bean
@Bean //将当前方法的返回值对象交给IOC容器管理, 成为IOC容器bean
public SAXReader saxReader(){
return new SAXReader();
}
}
解决方案2:在配置类中定义@Bean标识的方法
java@Configuration //配置类 (在配置类当中对第三方bean进行集中的配置管理)
public class CommonConfig {
//声明第三方bean
@Bean //将当前方法的返回值对象交给IOC容器管理, 成为IOC容器bean
//通过@Bean注解的name/value属性指定bean名称, 如果未指定, 默认是方法名
public SAXReader reader(DeptService deptService){
System.out.println(deptService);
return new SAXReader();
}
}
在方法上加上一个@Bean注解,Spring 容器在启动的时候,它会自动的调用这个方法,并将方法的返回值声明为Spring容器当中的Bean对象。
注意事项 :
通过@Bean注解的name或value属性可以声明bean的名称,如果不指定,默认bean的名称就是方法名。
如果第三方bean需要依赖其它bean对象,直接在bean定义方法中设置形参即可,容器会根据类型自动装配。
在日常的开发中,服务端对象的校验是非常重要的一个环节,比如:注册的时候:校验用户名,密码,身份证,邮箱等信息是否为空,以及格式是否正确,但是这种在日常的开发中进行校验太繁琐了,代码繁琐而且很多。Validator框架应运而生,它的出现就是为了解决开发人员在开发的时候减少代码的,提升开发效率。它专门用来做接口的参数校验,比如:密码长度、是否为空等等。
SpringBoot的validator校验框架支持
javax.validation.constraints
包下。说明: JSR303是一种规范,但是不干”实事”,由hibernate validation来进行处理,但是当需要进行自定义扩展校验时,hibernate validation的扩展太过于复杂,进而使用Spring Validation,增强了hibernate validation的可扩展性。
常用的注解
注解 | 作用数据类型 | 说明 |
---|---|---|
@Null | 任何类型 | 对象必须为空 |
@NotNull | 任何类型 | 对象不为空 |
@NotBlank | 字符串 | 对象不为空,字符串去掉前后空格后长度不为0 |
@NotEmpty | 字符串、集合、数组 | 对象不为空,且字符串长度不为0,集合、数组大小不为0 |
@AssertTrue | 布尔型 | 必须为true;null值有效,Boolean通过校验,boolean不可 |
@AssertFalse | 布尔型 | 必须为false;null 可通过校验 |
@Min(number) | 整型数 | 数值必须大于或等于指定的最小值 |
@Max(number) | 整型数 | 数值必须小于或等于指定的最大值 |
@DecimalMin(decimal) | 浮点型 | 数值必须大于或等于指定的最小值,内部使用BigDecimal定义数值对象;为 null 是校验通过;默认包含边界值 |
@DecimalMax(decimal) | 浮点型 | 数值必须小于或等于指定的最大值,内部使用BigDecimal定义数值对象;为 null 是校验通过;默认包含边界值 |
@Positive | 整型数 | 数值必须为正整数 |
@PositiveOrZero | 整型数 | 数值必须为正整数或0 |
@Negative | 整型数 | 数值必须为负整数 |
@NegativeOrZero | 整型数 | 数值必须为负整数或0 |
@Digits | 数值型或者字符串 | 作为数值其构成必须合法 |
@Digits(integer=, fraction=) | 数值型 | 数值必须符合指定的整数精度和小数精度 |
@Size(min=, max=) | 字符串、集合、数组 | 对象的大小在指定区间内;为 null 是校验通过 |
@Past | Date或者Calendar对象 | 必须是一个过去的日期 |
@PastOrPresent | Date或者Calendar对象 | 必须是一个过去或者当前的日期 |
@Future | Date或者Calendar对象 | 必须是一个将来的日期 |
@FutureOrPresent | Date或者Calendar对象 | 必须是一个将来或者当前的日期 |
@Pattern | 字符串 | 必须是规则正确的正则表达式 |
字符串 | 必须是Email类型;可以通过指定regexp和flags来自定义email格式;为null时算作通过验证 |
javax.validation.constraints
包仅仅定义了上面的一系列注解,真正的实现逻辑在 org.hibernate.validator
包中,在 SpringBoot 项目启动时一般都会自动引入 hibernate 包。
Maven 包:org.hibernate.validator.constraints
.0.18.Final注解 | 作用数据类型 | 说明 |
---|---|---|
@Length(min=, max=) | 字符串 | 字符串的长度在指定区间内 |
@Range(min=, max=) | 数值型 | 数值必须在指定闭区间内 |
@CreditCardNumber | 字符串 | 必须是通过Luhn校验和测试的信用卡号码 |
@URL | 字符串 | 必须是URL地址 |
@UniqueElements | 集合类 | 校验集合中的值都是唯一的,null 视为有效成员 |
xml<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
java@Data
public class UserVo {
@NotNull(message = "用户id不能为空")
private Long userId;
@NotBlank(message = "用户名不能为空")
@Length(max = 20, message = "用户名不能超过20个字符")
@Pattern(regexp = "^[\\u4E00-\\u9FA5A-Za-z0-9\\*]*$", message = "用户昵称限制:最多20字符,包含文字、字母和数字")
private String username;
@NotBlank(message = "手机号不能为空")
@Pattern(regexp = "^[1][3,4,5,6,7,8,9][0-9]{9}$", message = "手机号格式有误")
private String mobile;
@NotBlank(message = "联系邮箱不能为空")
@Email(message = "邮箱格式不对")
private String email;
@Future(message = "时间必须是将来时间")
private Date createTime;
}
java@RestController
@Api(description = "用户校验")
@RequestMapping("/user")
public class UserValiatorController {
@PostMapping("/valiator/reg")
public UserVo createUser(@RequestBody @Validated UserVo userVo) {
return userVo;
}
}
@Validated对**@Valid**进行了二次封装,但是二者有以下的区别:
如果校验不通过会报BindException或者MethodArgumentNotValidException或者ConstraintViolationException
java/**
* 处理参数校验异常
*/
@ExceptionHandler(value = {BindException.class, ValidationException.class, MethodArgumentNotValidException.class})
public Result handleValidatorException(@NotNull Exception e) {
StringBuilder sb = new StringBuilder("校验失败:[");
if (e instanceof BindException) {
BindException exception = (BindException) e;
BindingResult bindingResult = exception.getBindingResult();
for (FieldError fieldError : bindingResult.getFieldErrors()) {
sb.append(fieldError.getDefaultMessage()).append(";");
}
} else if (e instanceof ValidationException) {
ValidationException exception = (ValidationException) e;
sb.append(exception.getMessage());
} else if (e instanceof MethodArgumentNotValidException) {
MethodArgumentNotValidException exception = (MethodArgumentNotValidException) e;
sb.append(exception.getBindingResult().getFieldError().getDefaultMessage());
}
sb.append("]");
return Result.error(sb.toString());
}
AOP是Aspect Oriented Programming的缩写,意思是:面向切面编程,它是通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。
在AOP术语中,切面要完成的工作被称为通知,通知定义了切面是什么以及何时使用。
Spring切面有5种类型的通知,分别是:
连接点是在应用执行过程中能够插入切面的一个点,这个点可以是调用方法时、抛出异常时、修改某个字段时。
切点是为了缩小切面所通知的连接点的范围,即切面在何处执行。我们通常使用明确的类和方法名称,或者利用正则表达式定义所匹配的类和方法名称来指定切点。
切面是通知和切点的结合。通知和切点共同定义了切面的全部内容:它是什么,在何时和何处完成其功能。
引入允许我们在不修改现有类的基础上,向现有类添加新方法或属性。
织入是把切面应用到目标对象并创建新的代理对象的过程。
切面在指定的连接点被织入到目标对象中,在目标对象的生命周期里,有以下几个点可以进行织入:
Spring AOP构建在动态代理之上,也就是说,Spring运行时会为目标对象动态创建代理对象。
代理类封装了目标类,并拦截被通知方法的调用,再把调用转发给真正的目标bean。
当代理类拦截到方法调用时,在调用目标bean方法之前,会执行切面逻辑。
通过在代理类中包裹切面,Spring在运行期把切面织入到Spring 管理的bean中,也就是说,直到应用需要被代理的bean时,Spring才会创建代理对象。
因为Spring运行时才创建代理对象,所以我们不需要特殊的编译器来织入Spring AOP切面。
Spring只支持方法级别的连接点,如果需要字段级别或者构造器级别的连接点,可以利用AspectJ来补充Spring AOP的功能。
首先引入依赖
xml<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
<version>${spring-boot.version}</version>
</dependency>
然后定义一个切面如下
java/**
* 观众
* 使用@Aspect注解定义为切面
*/
@Aspect
public class Audience {
}
本文作者:peepdd864
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!