基础
#
特点
容器, 也容纳管理了第三方框架 # 目的是解耦框架api
轻量级,模块化,无(少)侵入
IOC(inversion of control)
容器用工厂装配对象并管理, 面向接口编程
优点
降低代码量
容易测试
最小侵入性松耦合
方式
Setter
构造器
静态工厂、实例工厂
DI(dependency injection)
容器向类添加属性 # 反射,用带参构造或set方法
AOP(aspect oriented programming)
# 继承是纵向组织,AOP横切入业务
# oop是静态概念,aop是动态概念(aop的切面可以替换或不使用)
动态代理实现切入代码
权限控制
事务管理
记录日志
概念
连接点:普通方法
切入点:名称满足条件的连接点
增强(通知)类:服务对象
# 切入点与增强是多对多的
切面:切入点 + 增强类 # 我们切入的是横切面
目标对象(服务对象):要注入的对象
通知
before # 执行前
after # 执行后
after-returning # 正常退出
after-throwing # 异常退出
around # 执行前后
代理对象(业务对象):被注入的对象
模块
dao, orm, aop, jee, web, core
结构
核心容器(core container)
Core # 最底层,资源访问,类型转换
Beans # IOC, DI, BeanFactory
Context # 以Core、Beans为基础,ApplicationContext。资源绑定,数据验证,国际化,JavaEE支持,容器生命周期,事件传播
EL # 表达式语言
AOP, Aspects # Aspects对AspectsJ集成, 功能多于spring asp
数据访问/集成
JDBC
ORM
OXM # object xml 映射
JMS # 消息与异步通信
事务
Web/Remoting
Web # ioc窗口,rmi, hessian,burlap, web service
Web-Servlet
Web-Struts
Web-Porlet # portal认证
Test
设计模式
代理
目标对象实现接口,使用Proxy
未实现接口,使用CGLIB
单例
bean默认单例
模板, 解决代码重复问题
RestTemplate, JmsTemplate, JpaTemplate
控制器
DispatherServlet对请求分发
视图帮助(view helper)
提供jsp标签、高效宏 帮助在视图中写代码
依赖注入
BeanFactory, ApplicationContext核心理念
工厂
BeanFactory
循环依赖问题
构造器, 正在创建在Bean池标记,创建完删除标记,标记冲突报错 # 所以用@Autowired决定注入时机,不写在构造方法里
单例, 三级缓存, 提前暴露使双方都可初始化
setter, 提前暴露bean
版本
2.5
注解
3.2
基于注解的注入测试类@RunW..
使用
#
jar包 :
核心包:/dist/modules # 或是/dist中的spring.jar包
core模块
beans
context
context-support
core
日志包:/lib/jakata-commons/commons-logging.jar
创建xml文件(最好在JavaBean的旁边)
绑定约束文件 /dist/resources/spring-beans-2.5.xsd
copy xml文件的头:/sample/petclinic/war/web-inf/app...xml
创建JavaBean.java
xml文件中配置bean # src下
<bean id="" class=""> # id值唯一,class指定 javaBean的类目录
<property name="" value="">
<property name="" ref="girlID"> # 引用类型,其中girlID为spring配置的bean Id
业务类中
ApplicationContext ac = new ClassPathXmlApplicationContext(new String [] {"配置xml文件路径"})
# 注意:ac创建时,其内部的JavaBean默认全部实例化一遍,并且全部注入了属性
## 该容器不用关闭
if(ac.containsBean("boyID")){
Boy boy = (Boy)ac.getBean("boyID");
}
api
#
ApplicationContext是一个接口,表示spring容器/ioc容器
ClassPathXmlApplicationContext # 只从类路径中读取xml配置文件(src/目录下能访问的路径)省略src/目录
FileSystemXmlApplicationContext # 文件路径 ,从src/开始
ac.getBean("boyID")
ac.containsBean("boyID") # 通过此方法去判断是否存在 ,而不是得到的是否为null
ac.destroy() # ac中的bean实例全部销毁
BeanFactory
# 基础IOC容器, 默认延迟初始化
DefaultListableBeanFactory
ApplicationContext
XmlBeanFactory # 根据xml中的定义加载bean
Spring-DAO # 提供规范, 翻译框架(JDBC<Hibernate,JPA等)异常为DataAccessException
@Repository 注解DAO类
Spring-JDBC # 模板类
DataSource
JdbcTemplate
JdbcDaoSupport # 对dao扩展, DataAccessExceptions异常翻译器
Spring-ORM # 统称,对各模块(JPA,JDO,Hibernate,iBatis,TopLink,OJB)实现了spring的集成类
把DataSource注入到SessionFactory或EntityManagerFactory等bean中 # jdbc不需要,因为jdbc直接使用DataSource
HibernateTemplate
HibernateDAOSupport # 继承它提供aop拦截器
Web
# 在ApplicationContext基础上, 提供web上下文和面向web的服务
ApplicationContext # 以BeanFactory为基础,容器启动后默认全部初始化绑定
FileSystemXmlApplicationContext # 指定文件
ClassPathXmlApplicationContext # 从classpath找设置
WebXmlApplicationContext
注解
#
applicationContext.xml
<context:annotation-config/>
<!-- 添加注解扫描功能,启动的时候哪些包要检查是否有注解 -->
<context:component-scan base-package="xxx" />
@Required # setter
@Autowired # setter、构造方法、变量
@Qualifier("dataSource) # 多类配置时,指定使用类
@Bean # 返回对象注册为bean
@Configuration # bean定义
@Service # 添加类名小写的spring bean id 也可以@Service(value="xx")自定义id
## action类前换成@Controller @Service也是可以的
@Scope(value="prototype")
# 工具类或其它组件类换成@Component 也可以@Service,如定时器TimerTask就是组件
@Resource # 按属性名注入资源
测试类
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext*.xml")
@PostConstruct # 类加载时运行的方法,相当于xml中配置的init-method
@PreDestroy # 类销毁前运行的方法
aop
applicationContext.xml
<aop:aspectj-autoproxy/>
@Aspect
# 注册一个类为切面
@Pointcut(value="execution(* cn.it.shop.service.impl.GoodsServiceImpl.save(..))")
# 配置切点表达式
private void testAop(){}
@AfterReturning(pointcut="execution(* cn.it.shop.service.impl.GoodsServiceImpl.save(..))")
# 配置通知,在通知中配置切点
@AfterReturning(value="testAop()")
# 配置通知,使用已经配置的切点
@Around(value="testAop()")
# 配置通知,使用已经配置的切点
@Around(value="execution(* cn.it.shop.service.impl.GoodsServiceImpl.queryByWord(..))")
# 配置通知,在通知中配置切点,注意这里没有pointcut,只有value
模块
#
监听器
#
原理
org.springframework.web.context.ContextLoaderListener中
this.contextLoader.initWebApplicationContext(event.getServletContext());
# 加载Spring 的配置文件 ,加载Application内置对象中
initWebApplicationContext方法中
this.context = createWebApplicationContext(servletContext, parent);
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
# 创建并存储spring的application内置对象到ServletContext中,属性名称是WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE
createWebApplicationContext方法中
wac.setConfigLocation(sc.getInitParameter(CONFIG_LOCATION_PARAM));
# 该类文件中有:public static final String CONFIG_LOCATION_PARAM = "contextConfigLocation";
# 获得web.xml中配置的context-param初始化参数:contextConfigLocation的内容,并加载spring配置文件
使用
servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
ApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(servletContext)
properties
#
spring xml配置文件中使用properties配置的属性
配置一个bean的class类是org.springframework.beans.factory.config.PreferencesPlaceholderConfigurer的单例bean
<property name="locations" value="classpath:public.properties" >
<array>
<value>classpath:conn.properties</value>
或
<property name="location" value="classpath:conn.properties" />
xml文件中用"${driver}"的方式引用properties中配置的属性
java类中用spring的注解注入properties配置的属性
# 要求必须是spring管理的类
bean中的class类换成org.springframework.beans.factory.config.PropertiesFactoryBean
id="Xxx"其它相同
java类的属性或set方法上添加注解:
@Value("#{public.basePath}#{public.filePath}")
# 其中public 是上面配置的bean的id(xml文件中注入属性的话用不到id,所以没有配置)
## '#{}'代表引用属性
## '.'可以用'[]'代替,如public[basePath]
## 字符串的拼接可以用+连接
# 第一次配置@Value的时候不会成功,改一次值再试就可以了
bean
#
创建bean的顺序
xml中按配置顺序的先后
注解中按照字母的顺序
生命周期
配置中定义<bean></bean>
初始化
配置文件中init-method
实现org.springframework.beans.factory.InitializingBean接口
调用
销毁
配置文件中destroy-method
实现org.springframework.bean.factory.DisposeableBean
scope
# 默认singleton
prototype # 每次产生新对象
singleton # 单例
request # 一个请求一个对象,只在ApplicationContext下有效
session # 一个session一个对象,只在ApplicationContext下有效
global-session # 一个全局session一个对象, 只在ApplicationContext下有效
内部bean
<property>或<constructor-arg>中定义的<bean>,通常匿名
注入对象
<list>
<set>
<map>
<props> # 键值都只能是string类型
自动装配
方式
no # 不自动装配,通过ref属性指定
byName
查找类中setter
容器中找id
报空指针
byType
容器中找类型 # 找到多个时抛异常
constructor
byType带构造函数参数
autodetect
先试constructor, 再试byType
写法
<bean>属性autowire="byName"
@Autowired
类型自动装配
加上@Qualifier(value="a")
@Resource(name="a")注解的name装配
byName装配
byType装配
配置
<?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/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<bean
id="userDaoID"
name=""
class="cn.itcast.javaee.spring.bean.life.UserDao"
init-method="getSession"
destroy-method="closeSession"
factory-method="getUserDao"
scope="singleton"
lazy-init="false"
parent="userDaoID"
abstract="true"
autowire="no">
<constructor-arg type="java.lang.Integer" index ref>
<value>2013</value>
<constructor-arg type="java.lang.Double">
<value>6000</value>
<property name="name" value="aa"></property>
<property name="name" ref="dateID" />
<property name="telSet">
<set>
<value>11</value>
<property name="cityList">
<list>
<value></value>
<property name="telCityMap">
<map>
<entry key="">
<value></value>
<property name="">
<props>
<prop key=""></prop>
标签、属性分析
bean标签:代表一个JavaBean
# 多个JavaBean配置时,先配置的先创建,先创建的后销毁
id:该JavaBean的唯一标识
name:可以和id一样用,但是name可以设置"/a"来绑定路径
class:该JavaBean的类路径
init-method:创建该JavaBean时运行的其中的方法
destroy-method:销毁该JavaBean时运行的其中的方法
# ClassPathXmlApplicationContext类的实例不监听销毁方法
## 用AbstractApplicationContext中的close()与registerShutdownHook()方法
## close()直接马上销毁,registerShutdownHook()方法会注册关闭钩子,监听容器的销毁
##
factory-method:创建该Bean的函数
得到接口实现类的方法
1.通过实现类的无参构造器 # 没有factory-method属性
2.没有无参构造器时,工厂静态方法创建实例
<bean id="userDaoID" factory-bean="daoFactoryID" factory-method="getUserDao">
# class中的内容是工厂类,而非UserDao类,factory-method是工厂类中返回UserDao类的静态方法
3.没有无参构造器时,工厂非静态方法创建实例
<bean id="daoFactoryID" class="cn.itcast.javaee.spring.bean.create.type3.DaoFactory">
<bean id="userDaoID" factory-bean="daoFactoryID" factory-method="getUserDao">
# 先实例化工厂(Spring 加载本xml文件默认实例化),然后静态方法的配置即可
scope:作用域
1.singleton(单例)是默认值,是单例的,会调用init destory方法
2.prototype(原型)每次创建一个实例, 调用init方法,但不调用destory方法(实例的维护 :javase程序交给jvm,javaee程序交给服务器)
lazy-init
1.false:为启动容器时立即创建一个实例 # singleton与prototype模式都会创建
2.true:启动时不加载,获取时创建
parent: 继承一个Spring bean(包含其中的所有属性)
# javaBean类中不必有实际的继承关系(但是有继承关系则必要配置parent)
abstract="true" : 配置此Bean为抽象Bean
autowire="no":自动装配,只能装配关联属性
# 还可以进行注解装配
byName 装配时根据bean中的每个属性名从spring中找id同名的bean,通过setter方法注入到该属性中
byType 根据bean中的每个属性的类型找到spring中映射该类型的bean进行装配,通过setter方法注入到该属性中
constructor 找满参构造器装载,构造器中的参数以byType方式注入
autodetect 先找构造器装载,再set方法注入。但实际使用中只能set方法注入
no 不注入
可以配置init-method与destroy-method属性来配置该bean创建和销毁时执行的方法
注入值 # 通过setter方法注入值
<constructor-arg type="java.lang.Integer" index ref>
<value>2013</value> # 传入构造方法参数注入值,,位置不能颠倒,不调用setter方法
## type是注入参数的类型,index是参数的标号,从0开始,ref是引用类型,有引用类型时不用<value>标签
<property name="name" value="aa"/> # 基本类型直接赋值(包括包装类型与String)
<property name="name" ref="dateID"/> # 引用类型,dateID是一个Spring Bean
## 可以直接引用Spring Bean 的id
<set>
<value></value> # set集合
<list>
<value></value> # list集合
<map>
<entry key="">
<value></value> # map集合
# 集合的值均可配置引用类型
<property name="">
<props>
<prop key=""></prop> # 属性类型,只能配置基本类型 (包括包装类型与String)
aop
#
实现 # 基于Aspectj
原理
启动容器时,创建目标对象与代理对象
<aop:config/>加载完时,通过cglib创建目标对象的代理对象,运行时产生
程序员-代理对象-代理方法-目标对象-目标方法-代理对象
使用
写类文件
1.jar包 aspectjweaver.jar/aspectjrt.jar/cglib-nodep-2.1_3.jar(动态代理包)
2.配置xml文件头,保留aop
<bean id class/> # 目标对象
<bean id="serviceBeanID" class /> # 配置增强对象
<aop:config> # 相当于创建了代理对象
<aop:pointcut id="xxxx" expression="" /> # 切入点表达式:expression="execution(public void addUser() throws java.lang.Exception)"
## 可以写成execution(public void 类名.*()),表示匹配所有方法
## execution(* *(..)) 第一个*是返回值,第二个*是方法,..表示参数不限
## 可以声明抛异常
## 条件命名为xxxx,升级连接点到切入点
<aop:aspect ref="serviceBeanID">
<aop:before method="writeLog" pointcut-ref="xxxx"/> # 前置增强,method是注入的方法,xxxx是增强的条件,只能写一个方法
<aop:after/>
<aop:after-returning/> # 方法返回后执行
<aop:after-throwing/> # 抛出异常时执行
<aop:around/> # 环线,执行目标方法前、后都执行,出错则之后的函数不执行
public void both(ProceedingJoinPoint pjp){ # ProceedingJoinPoint是连接代理对象 与目标对象的桥梁
open();
pjp.proceed(); # 执行目标代码
close();
}
# 目标方法出错,后置增强仍然执行,after-throwing执行,前置增强不执行,after-returning不执行
<aop:advisor advice-ref="txAdvice" pointcut-ref="xxxx"/> # 配置事务的切面
切入点表达式
execution(方法的修饰符 方法的返回值类型 方法所属的类 方法名 方法中参数列表 方法抛出的异常)
方法的修饰符: 支持通配符*,可省略
方法的返回值类型:支持通配符*,表示所有返回值,不可省
方法所属的类: 支持通配符*,可省略
方法名: 支持通配符*,表示所有方法,不可省
方法中参数列表: 支持通配符*,不可省
# *表示【一个】任意类型的参数
## ..表示零个或一个或多个任何类型的参数【提倡】
execution(方法的返回值类型 方法名(方法中参数列表)) # 一般形式
例如:
execution(public void add()throws Exception)
execution(public void add(..)throws Exception)
execution(public void add(*)throws Exception)
execution(* cn.itcast.web.spring.aop.UserDao.add(..))
execution(* add()throws Exception)
execution(public void *(..)throws Exception)
execution(public void a*(..)throws Exception):方法名以a字符开始
execution(public void *d(..)throws Exception):方法名以d字符结束
execution(* add())
execution(* *(..))
切点方法的编写
public void Xxx(JoinPoint joinPoint){
joinPoint.getTarget(); # 获取目标对象
joinPoint.getSignature(); # 获取当前连接点的方法信息
joinPoint.getArgs()[0]; # 获取当前连接点的第一个参数
..
}
public Goods Yxx(ProceedingJoinPoint joinPoint){
Object object = joinPoint.proceed(); # 得到连接点的返回值
..
return goods; # 本切面返回的数据会作为切点返回的数据返回给调用它的函数
}
dao
#
使用
1.xml文件中
<!-- 配置C3P0连接池 -->
<bean id="comboPooledDataSourceID" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql://127.0.0.1:3306/spring"/>
<property name="user" value="root"/>
<property name="password" value="root"/>
<property name="initialPoolSize" value="60"/>
<property name="acquireIncrement" value="5"/>
</bean>
<!-- 配置JdbcTemplate -->
<bean id="jdbcTemplateID" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="comboPooledDataSourceID"/>
</bean>
<!-- 配置UserDao -->
<bean id="userDaoID" class="dao.UserDao">
<property name="jt" ref="jdbcTemplateID"/>
</bean>
2.Dao中
private JdbcTemplate jt;
addUser()
String sql = "";
Object [] params = {user.getUsername(),user.getPassword()}
jt.update(sql,params);
批量:
for(int i = 0; i < ids.length; i++){
sqls[i] = "";
}
jt.batchUpdate(sqls);
查询一个
return (User)jt.queryForObject(sql,params,new RowMapper(){
public Object mapRow(ResultSet rs,int rowNum){ # rs是查询出来的结果集,rowNum是结果集的行号,从0开始
Integer id = rs.getInt("id");
User user = new User(id);
return user;
}
});
查询多个 # query方法把RowMapper帮助类中返回的user分别加入到list中,返回一个list
list = jt.query(sql,new RowMapper(){
public Object mapRow()
..
return bean;
})
分页
String sql = "select * from users limit ?,?";
Object[] params = {0,3};
jt.query(sql,params,new RowMapper(){
记录
jt.queryForInt(sql);
事务
#
TransactionInterceptor
transactionManager # 指定事务治理类
transactionAttributes # key方法名 value事务属性
注解
@Transactional(propagation = Propagation.REQUIRED)
手写 # TransactionDefinition
Public class BankServiceImpl implements BancService{
Private BanckDao bankDao;
private TransactionDefinition txDefinition;
private PlatformTransactionManager txManager;
public boolean transfer(Long fromId, Long toId, double amount) {
TransactionStatus txStatus = txManager.getTransaction(txDefinition);
boolean result = false;
try {
result = bankDao.transfer(fromId, toId, amount);
txManager.commit(txStatus);
} catch (Exception e) {
result = false;
txManager.rollback(txStatus);
System.out.println("Transfer Error!");
}
return result;
}
}
手写 # TransactionTemplate
public class BankServiceImpl implements BankService {
private BankDao bankDao;
private TransactionTemplate transactionTemplate;
public boolean transfer(final Long fromId, final Long toId, final double amount) {
return (Boolean) transactionTemplate.execute(new TransactionCallback(){
public Object doInTransaction(TransactionStatus status) {
Object result;
try {
result = bankDao.transfer(fromId, toId, amount);
} catch (Exception e) {
status.setRollbackOnly();
result = false;
System.out.println("Transfer Error!");
}
return result;
}
});
}
}
配置 # TransactionInterceptor
<bean id="transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor">
<property name="transactionManager" ref="transactionManager"/>
<property name="transactionAttributes">
<props>
<prop key="transfer">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
<bean id="bankServiceTarget" class="footmark.spring.core.tx.declare.origin.BankServiceImpl">
<property name="bankDao" ref="bankDao"/>
</bean>
<bean id="bankService" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="bankServiceTarget"/>
<property name="interceptorNames">
<list>
<idref bean="transactionInterceptor"/>
</list>
</property>
</bean>
配置 # TransactionProxyFactoryBean
<bean id="bankServiceTarget" class="footmark.spring.core.tx.declare.classic.BankServiceImpl">
<property name="bankDao" ref="bankDao"/>
</bean>
<bean id="bankService" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="target" ref="bankServiceTarget"/>
<property name="transactionManager" ref="transactionManager"/>
<property name="transactionAttributes">
<props>
<prop key="transfer">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
配置 # tx空间
<?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:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
<!--配置c3p0连接池-->
<!-- 配置JdbcTemplate类 -->
<!-- 配置Dao -->
<!-- 配置jdbc事务管理器 -->
<bean id="dataSourceTransactionManagerID" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="comboPooledDataSourceID"/>
</bean>
<!-- 配置事务增强(服务对象) -->
<tx:advice id="txAdvice" transaction-manager="dataSourceTransactionManagerID">
<tx:attribute>
<tx:method name="addUsers" # 可以用通配符"*users"
propagation="required" # 传播行为:事务开始、结束的时间。required 保证方法执行时事务已开始,事务开始时不创建,没有开始时创建
isolation="default" # 隔离级别
timeout="-1" # 事务超时,-1代表不超时,用数据库底层的配置
rollback-for:"java.lang.Exception" # 何时回滚
read-only="false" # 不只读
# name 方法名的匹配模式
# required : 外部存在事务,则加入外部事务,不存在则新建事务
# requires_new : 总是新建事务
# mandatory : 外部必须存在事务
# never : 外部不能存在事务
# supports : 外部存在则加入,不存在则不以事务方式运行
# not_supported : 总是非事务
# nested : 外部存在事务,嵌套执行,不存在则新建
# no-rollback-for 以逗号分隔异常,这些异常不会导致事务回滚
# rollback-for 导致事务回滚的异常
/>
<tx:attribute>
<!-- 配置AOP -->
<aop:config>
<aop:pointcut id="xxxx" expression="execution(public void *Users(..))"/>
##
任意公共方法的执行:
execution(public * *(..))
任何一个以“set”开始的方法的执行:
execution(* set*(..))
AccountService 接口的任意方法的执行:
execution(* com.xyz.service.AccountService.*(..))
定义在service包里的任意方法的执行:
execution(* com.xyz.service.*.*(..))
定义在service包和所有子包里的任意类的任意方法的执行:
execution(* com.xyz.service..*.*(..))
定义在pointcutexp包和所有子包里的JoinPointObjP2类的任意方法的执行:
execution(* com.test.spring.aop.pointcutexp..JoinPointObjP2.*(..))")
<!-- 将事务代码切入点addUser()方法中,从而产生事务 -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="xxxx"/>
</aop:config>
spring mybatis
#
基础
#与$
#相当于解析成引号, 防止sql注入
$变量引用, 不能防止sql注入,用于传入表名之类
特点
sql易维护,传参方便
orm
Mapper接口
方法名与配置id相同
输入输出参数类型对应定义的parameterType类型和resultType类型
类路径是配置的namespace
缓存
# 基于PerpetualCache的HashMap
一级缓存
存在本地
作用域为session, session flush后清空
二级缓存
可定义存储服务
作用域为namespace
配置
<cache/>
readOnly="true" 时,缓存实例单例,false时返回缓存拷贝
更新
create, update, delete后,作用域下所有select缓存clear
与hibernate区别
都通过SessionFactoryBuilder从配置生成SessionFactory, 再生成Session
都支持jdbc和jta
mybatis可细致优化sql, hibernate移植性好
mybatis学习成本低
mybatis本身缓存不好,hibernate对象维护和缓存好
hibernate dao层封开发简单(不用维护映射),crud方便
使用
编程
创建SqlSessionFactory
创建SqlSession
执行数据库操作
session.commit()
session.close()
导入ibatis jar包
配置文件
SqlMap.properties # 属性名可以修改
driver=oracle.jdbc.driver.OracleDriver
url=jdbc:oracle:thin:@127.0.0.1:1521:orcl
username=root
password=root
SqlMapConfig.xml # 总配置文件
<sqlMapConfig>
<properties recource="SqlMap.properties"/>
<transactionManager type="JDBC">
<dataSource type="SIMPLE">
<property value="${driver}" name="JDBC.Driver"/>
<property value="${url}" name="JDBC.ConnectionURL"/>
<property value="${username}" name="JDBC.Username"/>
<property value="${password}" name="JDBC.Password"/>
</dataSource>
<sqlMap resource="Student.xml"/>
</transactionManager>
</sqlMapConfig>
Student.xml # 映射xml文件
<sqlMap>
<typeAlias alias="Student" type="com.Student"/>
<select id="selectAllStudent" resultClass="Student">
select * from Student
</select>
</typeAlias>
</sqlMap>
辅助类Student.java # 要求有无参构造方法
private sid = 0;
private String name = null;
private String major = null;
private Date birth = null;
private float score = 0;
Xxx.java
private static SqlMapClient sqlMapClient = null;
static{
Reader reader = Resources.getResourceAsReader(总配置文件);
sqlMapClient = SqlMapClientBuilder.buildSqlMapClient(reader);
reader.close();
}
public List<Student> queryAllStudent(){
List<Student> studentList = sqlMapClient.queryForList("selectAllStudent");
return studentList;
}
@Test
public void testHere(){
for(Student student: this.queryAllStudent()){
System.out.println(student.getName);
}
}
配置
sqlMapConfig.xml
mybatis加载属性顺序
properties中property中的属性
properties中resource或url中的属性
parameterType中传递一属性
# properties中配的属性会影响到mapper.xml中${}的sql拼接,因为都是ognl
配置标签
properties
settings
# ibatis有性能优化的参数,mybatis会自动调优,不用设置了
typeAliases
# 针对parameterType和resultType指定的类型定义别名
# java.lang.Integer在mybatis中默认别名为int
typeHandlers
# 类型处理器,jdbc类型和java类型的转换
## 一般mybatis提供的类型处理器够用了
objectFactory
plugins
environments
mappers
mapper.xml
内容
#{}接收简单类型, pojo的ognl属性注入
${}是字符串的拼接
SELECT * FROM USER WHERE id=#{id}
SELECT * FROM USER WHERE username LIKE '%${value}%'
# sql 注入
输入输出映射
parameterType
java类型
hashmap
# #{key}来取value
pojo
包装类型
resultType
# 指定一条数据的类型,在java方法的返回类型中list或pojo来体现数据条数
# mybatis判断mapper代理中使用selectOne或者selectType
pojo
# 返回字段可以是别名,但要与pojo中的属性名相同
## 如果有记录返回但没有pojo中匹配的属性名对应,则直接不创建该对象
java类型
# 在返回结果只有一行一列时,可以是简单类型
hashmap
# key是字段的字,value是字段的值
## 多条数据时,list里面存hashmap
resultMap
# 查询出来的字段名与pojo属性名不一致
定义resultMap
使用resultMap
动态sql
<where>
<if>
sql片段<sql>
<foreach>
高级映射
缓存
逆向
要求
1. mapper.xml中namespace 写mapper接口
<mapper namespace="com.otr.tea.mapper.UserMapper">
2. mapper.java中方法名与mapper.xml的statementid一致
3. mapper.java中方法的输入类型与mapper.xml中的parameterType一致
# 由于传入的参数只有一个,所以用包装类型的pojo来传多个参数,不利于业务层的可扩展性
4. mapper.java中方法的返回类型与mapper.xml中的resultType一致
机制
如果Mapper中返回类型为pojo, 则调用selectOne, 如果是List, 则调用selectList
api
sqlSessionFactory
sqlSession # 是线程不安全的,因为它的类中有数据和属性
# 是多例的,在方法中局部变量使用
Executor # 执行器操作数据库(基本执行器,缓存执行器)
mapped statement # 封装sql语句、输入参数、输出结果类型
例子
InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
# Resources是mybatis提供的资源加载类
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession = factory.openSession();
User user = sqlSession.selectOne("test.findUserById", 1);
# selectList()
# insert("test.insertUser", user)
## sqlSession.commit();
## user.getId() 会主键返回
### mysql中LAST_INSERT_ID()在insert语句后接着执行可以得到刚刚自成的id
sqlSession.close();
案例
返回id
mysql
<insert id="insert" parameterType="com.test.User" keyProperty="userId" useGeneratedKeys="true" >
oracle
<insert id="insert" parameterType="com.test.User">
<selectKey resultType="INTEGER" order="BEFORE" keyProperty="userId">
SELECT SEQ_USER.NEXTVAL as userId from DUAL
</selectKey>
insert into user (user_id, user_name, modified, state)
values (#{userId,jdbcType=INTEGER}, #{userName,jdbcType=VARCHAR},
#{modified,jdbcType=TIMESTAMP}, #{state,jdbcType=INTEGER})
</insert>
spring struts2
#
原理
tomcat启动日志:没有整合时不能加载struts-plugin.xml(spring-struts-plugin.jar包中的配置文件 )
struts中struts-default中常量配置加载com.opensymphony.xword2.ObjectFactory类作为默认struts创建action的类
加载后struts-plugin.xml 中 修改了常量为struts-spring-plugin中的类来创建struts的类,也就是整个struts2创建action类的类被更改了
整合
jar包 struts2/lib/struts2-spring-plugin-2.3.1.1.jar # 为了在struts的xml配置文件中的class找spring 的容器
配置web.xml # \samples\petclinic\war\WEB-INF\web.xml目录下有示例
<listener> # 监听器,web程序启动时加载spring bean
<listener-class>org.springframework.web.context.ContextLoaderListener
<context-param> # (可选)配置spring 配置文件的路径,
## 从示例文件中查到,默认文件目录是/WEB-INF/applicationContext.xml(我们示例文件也是从源码/simple项目下的这个开头的文件中找的)
<param-name>contextConfigLocation
<param-value>/WEB-INF/classes/struts2/xxx.xml
UserAction 中 # 不用值栈是因为通用性
spring.xml # action类由spring 产生
<bean id="userActionID" class="" scope="prototype"/>
struts2的配置文件中,替换class属性为spring beanid,其它一样
总结
spring的web配置是由下向上,一个个依赖注入的过程
comboPooledDataSourceID ->
localSessionFactoryBeanID ->
hibernateTemplateID ->
SysStaffDaoID ->
SysStaffServiceID ->
SysStaffActionID ->
struts.xml配置中的<action class="SysStaffActionID">
最后给Dao中的方法加入事务
spring mvc
#
原理
DispatchServlet doService()捕获请求, doDispatch()用HandlerMapping映射url得到HandlerExcutionChain(执行链, 包括拦截器和handler)
handler getHandlerAdapter得到适配器来处理handler, 返回ModelAndView
# HandlerAdapter分三类: Servlet、Controller, HttpRequest
DispatchServlet用ViewResolver(视图解析器)解析ModelAndView成View
# ModelAndView是逻辑视图,DispatchServlet转化成视图View
返回View
与struts2区别
spring mvc方法对应请求, struts2是类
spring mvc请求是方法调用,struts2创建Action实例
spring mvc用aop处理请求,struts2用独有的拦截器(interceptor)
spring mvc入口是servlet, struts2入口是filter
spring mvc集成ajax(@ResponseBody), struts2需要插件
spring mvc验证支持JSR303, struts2不支持
spring mvc与spring无缝
spring mvc不需要配置
注解
@RequestMapping # url映射
@RequestBody # 转换参数到对象
@ResponseBody # 返回对象转json
开启注解处理器
springmvc.xml
<mvc:annotation-driven>
乱码问题
post
web.xml中配置CharacterEncodingFilter
get
tomcat配置文件修改项目编码
new String(Request.getParameter("a").getBytes("ISO8859-1"), "utf-8")
quartz:定时器
#
执行:ApplicationContext类加载后自动执行
导包:quartz-all.jar包 与 commons-collections.jar包 与 commons-logging.jar
xml配置:
<!-- 任务类 ,其中有个叫execute的方法-->
<bean id="myTaskID" class="jee.quartz.MyTask"/>
<!-- spring提供专用于定时任务类 -->
<bean id="methodInvokingJobDetailFactoryBeanID" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<!-- 要定时执行的实例的spring bean id -->
<property name="targetObject">
<ref bean="myTaskID"/>
</property>
<!-- spring bean中定时执行的方法 -->
<property name="targetMethod">
<value>execute</value>
</property>
</bean>
<!-- spring提供专用于任务频率类,给上面的任务指定频率 -->
<bean id="cronTriggerBeanID" class="org.springframework.scheduling.quartz.CronTriggerBean">
<property name="jobDetail">
<ref bean="methodInvokingJobDetailFactoryBeanID"/>
</property>
<property name="cronExpression">
<value>0 0/1 * * * ?</value>
</property>
</bean>
<!-- spring提供的专用于任务频率工厂类 -->
<bean id="schedulerFactoryBeanID" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<ref bean="cronTriggerBeanID"/>
</property>
</bean>
任务频率cronTriggerBean的配置:
cron解析器:
反斜线(/)字符表示增量值。例如,在秒字段中“5/15”代表从第 5 秒开始,每 15 秒一次。
问号(?)字符和字母 L 字符只有在月内日期和周内日期字段中可用。问号表示这个字段不包含
具体值。
所以,如果指定月内日期,可以在周内日期字段中插入“?”,表示周内日期值无关紧要。字母
L 字符是 last 的缩写。放在月内日期字段中,表示安排在当月最后一天执行。在周内日期字
段中,如果“L”单独存在,就等于“7”,否则代表当月内周内日期的最后一个实例。所以“0L”
表示安排在当月的最后一个星期日执行。
在月内日期字段中的字母(W)字符把执行安排在最靠近指定值的工作日。把“1W”放在月内
日期字段中,表示把执行安排在当月的第一个工作日内。
井号(#)字符为给定月份指定具体的工作日实例。把“MON#2”放在周内日期字段中,表示把任
务安排在当月的第二个星期一。
星号(*)字符是通配字符,表示该字段可以接受任何可能的值。
顺序:秒 分 时 日 月 周 年(年可以忽略)
例子
0 0 10,14,16 * * ? 每天上午10点,下午2点,4点
0 0/30 9-17 * * ? 朝九晚五工作时间内每半小时
0 0 12 ? * WED 表示每个星期三中午12点
0 0 12 * * ? 每天中午12点触发
0 15 10 ? * * 每天上午10:15触发
0 15 10 * * ? 每天上午10:15触发
0 15 10 * * ? * 每天上午10:15触发
0 15 10 * * ? 2013 2013年的每天上午10:15触发
0 * 14 * * ? 在每天下午2点到下午2:59期间的每1分钟触发
0 0/5 14 * * ? 在每天下午2点到下午2:55期间的每5分钟触发
0 0/5 14,18 * * ? 在每天下午2点到2:55期间和下午6点到6:55期间的每5分钟触发
0 0-5 14 * * ? 在每天下午2点到下午2:05期间的每1分钟触发
0 10,44 14 ? 3 WED 每年三月的星期三的下午2:10和2:44触发
0 15 10 ? * MON-FRI 周一至周五的上午10:15触发
0 15 10 15 * ? 每月15日上午10:15触发
0 15 10 L * ? 每月最后一日的上午10:15触发
0 15 10 ? * 6L 每月的最后一个星期五上午10:15触发
0 15 10 ? * 6L 2014-2018 2014年至2018年的每月的最后一个星期五上午10:15触发
0 15 10 ? * 6#3 每月的第三个星期五上午10:15触发
0/1 * * * * ? 每秒钟触发一次
0 0/1 * * * ? 每分钟解发一次
0 0 0/1 * * ? 每小时解发一次
远程调用
#
# rmi:remote message invoke
服务端
1.自定义接口IServer,自定义抽象方法int rax(int)
2.写接口实现类ServerImpl
3.配置spring.xml 文件
<!-- 服务端实现类 -->
<bean id="serverImplID" class="jee.server.ServerImpl"/>
<!-- spring提供的专用于RMI服务端注册器 -->
<bean id="rmiServiceExporterID" class="org.springframework.remoting.rmi.RmiServiceExporter">
<property name="serviceInterface">
<value>jee.server.IServer</value>
</property>
<property name="service">
<ref bean="serverImplID"/>
</property>
<property name="serviceName">
<value>XXXX</value>
</property>
</bean>
客户端
配置spring.xml 文件
<!-- spring提供专用于RMI远程服务代理工厂类 -->
<bean id="rmiProxyFactoryBeanID" class="org.springframework.remoting.rmi.RmiProxyFactoryBean">
<!-- 协议名://远程提供服务的IP地址:提供服务的端口/提供服务的名称 -->
<property name="serviceUrl">
<value>rmi://127.0.0.1:1099/XXXX</value>
</property>
<property name="serviceInterface">
<value>jee.client.IServer</value>
</property>
</bean>
执行:
服务端加载 ApplicationContext类
客户端
加载 ApplicationContext类 ac
ac.getBean方法中得到RmiProxyFactoryBean实际类型(可变类型)的实例,强转成服务端自定义的接口IServer的实现类(实现类由服务器决定)
执行IServer实现类中的方法int rax(int),实现了远程调用
websocket
#
handler
extends TextWebSocketHandler
@Override
handleTextMessge() # 处理client.send()的数据
@Override
afterConnectionEstablished(WebSocketSession) # 连接事件
@Override
handleTransportError() # 出错事件
@Override
afterConnectionClosed() # 断开事件
@Override
supportsPartialMessages() # 并行处理
config
@EnableWebSocket
implements WebSocketConfigurer
@Override
registerWebSocketHandlers()
registry.addHandler(handler, "/ws") # 路由handler
client
extends WebSocketClient
constructor(uri)
super(new URI(uri))
@Override
onOpen()
@Override
onClose()
@Override
onError()
@Override
onMessage()
service
init()
client = new Client("ws://127.0.0.1:8001/ws")
client.connectBlocking()
send()
while(!client.getReadyState().equals(ReadyState.OPEN)){
log("connecting")
}
client.send("")
runner
implements ApplicationRunner
run()
service.init()
spring boot
#
介绍
减少配置, 习惯大于配置
支持groovy, gradle
命令
java -jar xxx.jar
--server.port=8080 # --后内容,相当于application.yml设置
--spring.profiles.active=two # 选择applicaton-two.yml配置
基础文件
#
目录
src
main
java
com.outrun
XxxApplication
resources
static/
templates/
application.properties
application.yml
webapp
WEB-INF
test
java
com.outrun
XxxApplicationTests
pom.xml
XxxApplication.java # 程序入口
@SpringBootApplication # 类,组合@Configuration, @EnableAutoConfiguration, @ComponentScan
@EnableAutoConfiguration根据jar包依赖自动配置
扫描该注解同级下级包的Bean
application.yml # application.yml或application.properties, 放在src/main/resources或config目录下
pom.xml
配置
#
区分环境
application-{profile}.properties # profile比如是dev, test, prod
设置spring.profiles.active=dev来区分
加载顺序 # 为了外部人员维护,可覆盖定义
命令行
SPRING_APPLICATION_JSON环境变量, json格式
java:comp/env的JNDI属性
java系统属性 # System.getProperties()查看
系统环境变量
random.*配置的随机属性
jar包外文件名, 如application-{profile}.properties
jar包内文件名
@Configuration注解类中,@PropertySource修改的属性
SpringApplication.setDefaultProperties定义的内容
application.yml
--- # ---分隔多个配置,这里相当于建立了application-two.yml文件
spring:
profiles: two
---
spring:
profiles
active: dev # 配置环境, 加载applicaton-dev.yml
application:
name: app1
pom.xml
<packaging>jar</packaging> # 不用war包部署, 嵌入了tomcat, jar可服务
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId> # 提供spring boot基础依赖和默认配置
<relativePath/> # 从仓库查找parent
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId> # 方便启动停止应用, 如mvn spring-boot:run
</plugin>
</plugins>
</build>
注解
#
@SpringBootApplication # spring boot 启动类
组合了@Configuration, @EnableAutoConfiguration, @ComponentScan
ApplicationRunner # 继承该类,注解@Component, 随容器启动运行
run()
插件
#
maven
#
命令
mvn spring-boot:run
pom.xml
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId> # 方便启动停止应用, 如mvn spring-boot:run
</plugin>
组件
#
starter POMs # spring-boot-starter开头的组件
子域
#
NamedContextFactory
#
class Spec1 implements NamedContextFactory.Specification {
@Override
public String getName(){}
@Override
public Class<?>[] getConfiguration(){}
}
public class MyFactory extends NamedContextFactory<Specification1> {
public MyFactory(Class<?> clazz) {
super(clazz, "my", "my.name")
}
}
@Configuration
public class Config0 {
@Bean
Bean0 getBean(){
return new Bean0()
}
}
parent = new AnnotationConfigApplicationContext()
parent.register(Config0.class)
parent.refresh()
factory = new MyFactory(Config00.class)
factory.setApplicationContext(parent)
spec1 = new Spec1("1", new Class[]{Config1.class})
factory.setConfigurations(List.of(spec1))
factory.getInstance("1", Bean0.class) // 子域共享
factory.getInstance("1", Bean00.class) // 子域复制
factory.getInstance("1", Bean1.class)
spring
#
配置
随机数用${random}
${random.value} 字符串
${random.int} int
${random.long} long
${random.int(10)} 10以内int
${random.int[10,20]} 10到20 int
pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
application.yml
aa
bb: 1 # 可用properties类管理属性
xxx: 1 # 自定义value
配置文件中用"${xxx}"引用
类中用@Value("${xxx}")注入到属性
SpEL中用"#{xxx}"引用
AaProperties.java
@Component
@ConfigurationProperties(prefix = "aa")
public class AaProperties {
private String bb;
...getter和setter...
}
注解
@Configuration
@PropertySource(value = "classpath:test.properties") # 加载文件, 配合@ConfigurationProperties注入属性
@EnableConfigurationProperties({ConfigBean.class, User.class}) # 加载bean, 配合@Autowired注入
基础
注解
@Value("${xxx}")
@Autowired # 装载bean
@Bean # 实例化Bean, 属性名为方法名
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
相当于
RestTemplate restTemplate = new RestTemplate();
@Scope(ConfigurableBeanFactory.SCOPE_SINGLETON) # 生命周期
singleton # 单例
prototype # 多例
request # web程序ContextApplication用, 随请求创建
session # web程序ContextApplication用, 随session创建
global session # porlet的global用, 其它用降级为session
@EventListener(XxxEvent.class) # 修饰方法, 外部publishEvent()时触发
实体
注解
@Entity # 修饰bean类
@Id # id属性
@GeneratedValue(strategy=GenerationType.AUTO) # 自增属性
@Column(nullable = false, unique = true)
组件
注解
@Component
@ConfigurationProperties(prefix = "my") # 注入properties对应名称的属性
dao
注解
@Repository # 修饰类
service
注解
@Service # 修饰类
@PostConstruct # 修饰方法, 加载servlet时, init()前执行
@PreDestroy # 修饰方法, 销毁servlet时, destroy()后执行
controller
注解
@Controller # 修饰类
测试
#
pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
注解
@Before
@Test
@RunWith(SpringRunner.class) # 修饰类, 测试spring
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) # 修饰类, 测试spring boot
@LocalServerPort # 注入端口号
@AutoConfigureMockMvc # 使用mockMvc, 用@Autowired注入MockMvc
@WebAppConfiguration # 模拟ServletContext
XxxApplicationTests.java # junit测试
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = XxxApplication.class)
@WebAppConfiguration
public class XxxApplicationTests {
private MockMvc mvc;
@Before
public void setUp() throws Exception {
mvc = MockMvcBuilders.standaloneSetup(new XxxController()).build();
}
@Test
public void hello() throws Exception {
mvc.perform(MockMvcRequestBuilders.get("/hello").accept(MediaType.APPLICATION_JSON))
.addExpect(status().isOk())
.addExpect(content().string(equalTo("hello")));
}
}
数据库
#
pom.xml
spring-boot-starter-jdbc
spring-boot-starter-data-jpa # spring data JPA
application.yml
jpa:
generate-ddl: false
show-sql: true
hibernate:
ddl-auto: none # create时, 第一次create之后update
datasource:
platform: h2
schema: classpath:schema.sql # 建表
data: classpath:data.sql # 数据
注解
@Transactional # 修饰方法,开启事务,或在事务中
mybatis
#
pom.xml
<!-- mybatis -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.1.1</version>
</dependency>
<!-- mysql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.21</version>
</dependency>
application.yml
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/outrun?characterEncoding=UTF-8
username: root
password: asdf
driver-class-name: com.mysql.jdbc.Driver
jpa:
hibernate:
ddl-auto: update # 新建连接必要
mybatis:
mapper-locations: classpath:mapper/*.xml # 指定mapper.xml位置
mapper.xml # 用mbg生成
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.outrun.erp.mapper.UserMapper">
<resultMap id="BaseResultMap" type="com.outrun.erp.entities.User">
<id column="id" jdbcType="BIGINT" property="id" />
<result column="name" jdbcType="VARCHAR" property="name" />
</resultMap>
<select id="selectUserById" parameterType="long" resultMap="UserMap">
SELECT name FROM user WHERE id=#{userId}
</select>
<insert id="inserUser">
<selectKey keyProperty="id" resultType="int" order="BEFORE">
select field1 from seq1
</selectKey>
</insert>
<sql id="userColumns">
${alias}.id, ${alias}.username
</sql>
<select id="selectColumns" resultType="map">
select
<include refid="userColumns"><property name="alias" value="tb1"/></include>
from tb1
</select>
<select id="dynamicSql" resultType="User">
select * from user
where state = 0
<if test="title != null">
and title like #{title}
</if>
<choose>
<when test="title != null">
and title like #{title}
</when>
<when test="author != null and author.name != null">
and author_name like ${author.name}
</when>
<otherwise>
and featured = 1
</otherwise>
</choose>
<foreach item="item" index="index" collection="list" open="(" separator="," close=")">
#{item}
</foreach>
<trim prefix="where" prefixOverrides="and | or">
...
</trim>
<bind name="a" value="'%' + _data.getTitle() + '%'" />
select * from blog
where title like #{a}
</select>
<update>
update User
<set>
<if test="username != null">username=#{username},</if>
</set>
</update>
<cache> # 该命名空间缓存
<cache-ref> # 引用其它命名空间缓存
<delete>
<resultMap>
<constructor> # 构造方法
<idArg> # id参数, 标记id帮助提高性能
<arg> # 普通参数
</constructor>
<id> # 标记id帮助提高性能
<result> # 普通字段
<association> # 关联
<collection> # 结构体
<discriminator> # 自动映射
</resultMap>
</mapper>
mapper/UserMapper
@Mapper # 如果扫描mapper.xml,不用加@Mapper
public interface UserMapper {
List<User> selectUserById(@Param("userId") long userId)
@Select("select * from user")
List<User> findAll();
}
entities/User
public class User {
private Integer id;
private String name;
...getter, setter...
}
注解
@Table(name = "user") # 修饰类,指定表
@Id # 修饰属性, 指定主键
@Column(name = "name") # 修饰属性, 指定字段
@Mapper # 修饰类
@Select("select * from user") # 修饰方法
@Param("userId") # 修饰参数
api
SqlSessionFactory
build
openSession # 重载事务方法
SqlSesion
selectOne()
selectList()
selectMap()
insert()
update()
delete()
commit()
rollback()
clearCache()
close()
Mapper
o->
@Insert("insert into tb1(id, name) values(#{id}, #{name})")
@SelectKey(statement="next value", keyProperty="id", before=true, resultType=int.class)
int insertTable1(String name)
SQL
INSERT_INTO()
VALUES()
o->
new SQL(){{
SELECT("a.name");
SELECT("a.age");
FROM("tb1 a");
WHERE("a.name like ?");
}}.toString()
LogFactory
useSlf4jLogging()
useLog4jLogging()
useStdOutLogging()
web
#
# 用的spring mvc
pom.xml
<dependency>
<groupId>org.springframework.boot</groupId> # web模块, 有tomcat, spring mvc
<artifactId>spring-boot-starter-web</artifactId> # 测试模块, 有JUnit, Hamcrest, Mockito
</dependency>
application.yml
server
port: 8080 # 默认8080
servlet
context-path: /hello # uri前缀
静态资源
默认映射public, resources, static到/
注解
控制器
@RestController # 修饰类, 组合@Controller与@responseBody
@RequestMapping("/index") # 修改类或方法, url
@GetMapping("/{id}") # 相当于@RequestMapping(method=RequestMethod.GET)
@PostMapping
@PutMapping
@DeleteMapping
@PatchMapping
@CrossOrigin # 修饰方法, 允许跨域
@RequestBody # 修饰方法, 解析body到参数
@PathVariable Long id # 修饰参数, 接收url参数
内置对象
ServerProperties # 单例可@Autowired, 存端口之类属性
自实现
XxxController.java
@RestController
public class HelloController {
@RequestMapping("/hello")
public String index() {
return "hello";
}
}
日志
#
application.yml
logging:
level:
root: INFO
org.hibernate: INFO
org.hibernate.type.descriptor.sql.BasicBinder: TRACE
org.hibernate.type.descriptor.sql.BasicExtractor: TRACE
注解
@Slf4j # 修饰类,其中可直接用log变量
@EnableSwagger2 # 修饰类
@Api(tags = "") # 修饰类, 文档
@ApiModel("") # 修饰类
@ApiModelProperty(") # 修饰属性
@ApiOperation(value="", notes="") # 修改方法, 文档
@ApiIgnore # 修饰方法, 文档忽略
jackson
#
注解
@JsonInclude # 修饰类, 序列化时包含
@JsonInclude(JsonInclude.Include.NON_EMPTY) # null或""时不序列化
@JsonIgnore # 修饰属性
Scheduled
#
scheduled
注解
@Scheduled # 修饰方法, 定时调度
@Scheduled(initialDelay = 1000, fixedRate = 1000)
类
@Configuration
implements SchedulingConfigurer # 配置类
configureTasks(ScheduledTaskRegistrar)
registrar.setScheduler(Executors.newScheduledThreadPool(2)); # worker池
async
注解
@Async # 修饰方法, 异步调用
类
implements AsyncUncaughtExceptionHandler # 处理@Async异常
@Override
public void handleUncaughtException()
@Configuration
@EnableAsync
implements AsyncConfigurer
@Bean
@Override
public Executor getAsyncExecutor()
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() # 处理无返回值@Async方法异常
return handler
使用
@Async
Future<String> fetch(){
return new AsyncResult<String>("")
}
future = fetch()
try{
future.get()
}
热部署
#
pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional> # 热部署
</dependency>
application.yml
spring:
devtools:
restart:
enabled: true
additional-paths: src/main/java
jsp
#
pom.xml
<!-- servlet依赖. -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
</dependency>
<!-- tomcat的支持.-->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<scope>provided</scope>
</dependency>
application.yml
spring:
mvc:
view:
prefix: /WEB-INF/views/
suffix: .jsp
controller类
@Controller
public class XxxController {
@RequestMapping("/xxx")
public String xxx(Model m) {
m.addAttribute("a", 1);
return "view1";
}
}
src/main/webapp/WEB-INF/views/view1.jsp
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
jsp ${a}
lombok
#
注解
@Builder # 修饰类, 可build方式设置属性
@Getter # 修饰类, 生成getter
@Setter # 修饰类, 生成setter
@ToString # 修饰类, 生成toString方法
@Data # 修饰类, 注入getter, setter, toString
@NoArgsConstructor # 修饰类, 生成无参构造方法
@AllArgsContructor # 修饰类, 生成带所有参数的构造方法
@RequiredArgsConstructor # 修饰类, 生成带常量、@NotNull修饰变量参数的构造方法
@RequiredArgsConstructor(onConstructor_ = @Autowired) # 构造类时,自动对private final 属性@Autowire
remote shell
#
pom.xml
spring-boot-starter-remote-shell
actuator
#
pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
原生端点
应用配置类
/autoconfig # 自动化配置详情
positiveMatches # 成功
negativeMatches
/beans # 所有bean
/configprops # 属性
/env # 环境属性
/mappings # spring mvc映射关系
/info # 自定义信息,默认为空,用info前缀定义
度量指标
/metrics # 程序信息,内存、线程、gc等
nonheap.* # 非堆内存
gauge.* # http请求性能,如gauge.response表示上次延迟
counter.* # 记录累计,如counter.status.200表示返回200的次数
/metrics/{name} # 查看某项
/metrics/mem.free
/health # 启动状态,磁盘空间
DiskSpaceHealthIndicator # 低磁盘空间
DataSourceHealthIndicator # DataSource连接是否可用
MongoHealthIndicator
RabbitHealthIndicator
RedisHealthIndicator
SolrHealthIndicator
/dump # 线程信息
/trace # 跟踪信息
操作控制 # 用属性配置开启
/shutdown # 关闭端点
通过endpoints.shutdown.enabled=true开启
自定义counter统计
@Autowired
private CounterService counterService;
counterService.increment("didispace.hello.count")
自定义health检测器
@Component
public class RocketMQHealthIndicator implements HealthIndicator {
private int check(){}
@Override
public Health health() {
int errorCode = check();
if (errorCode !=0) {
return Health.down().withDetail("Error Code", errorCode).build();
return Health.up().build();
}
}
}
spring boot admin
#
application.yml
spring:
application:
name: erp-admin-server
boot:
admin:
routes:
endpoints: env,metrics,dump,jolokia,info,configprops,trace,logfile,refresh,flyway,liquibase,heapdump,loggers,auditevents,hystrix.stream
endpoints:
health:
sensitive: false
enabled: true
actuator:
enabled: true
sensitive: false
beans:
sensitive: false
enabled: true
spring initializer
#
介绍
生成spring基础项目
spring security
#
配置
application.yml
security:
basic:
enabled: false # 禁用security
注解
@EnableWebSecurity # 修饰WebSecurityConfigurerAdapter, 开启web验证
@EnableGlobalMethodSecurity(prePostEnabled = true) # 修饰WebSecurityConfigurerAdapter, 开启方法验证
@PreAuthorize # 修饰controller方法
api
Subject # 主体数据结构, 如用户
SecurityManager # 安全管理器, 管理所有subject
UserDetails
getAuthorities()
getUsername()
getPassword()
isAccountNonExpired()
isAccountNonLocked()
isCredentialsNonExpired()
isEnabled()
GrantedAuthority
getAuthority()
WebSecurityConfigurerAdapter
configure(HttpSecurity) # 验证请求
configure(AuthenticationManagerBuilder) # 验证数据,需要授权服务配置AuthenticationManager
userDetailService
passwordEncoder
authenticationManagerBean() # 指定管理bean
spring security oauth2
#
pom.xml
spring-cloud-starter-oauth2
结构
OAuth2 Provider
Authorization Service # 授权服务
Resource Service # 资源服务
Spring Security过滤器
/oauth/authorize # 授权
/oauth/token # 获取token
授权服务
applicatoin.yml # server
security:
oauth2:
resource:
filter-order: 3
注解
@EnableAuthorizationServer # 修饰AuthorizationServerConfigurerAdapter, 开启授权服务
api
AuthorizationServerConfigurerAdapter # 授权服务配置
configure(ClientDetailsServiceConfigurer) # 客户端信息
clientId
secret
scope
authorizedGrantTypes # password, refresh_token, client_credentials
authorities # 具体权限
configure(AuthorizationServerEndpointsConfigurer) # 使用token的服务
authenticationManager # 密码认证
authenticate(Authentication)
userDetailService # 获取用户数据
loadUserByUsername(String)
authorizationCodeServices # 验证码
implicitGrantService
tokenGranter
tokenStore
InMemoryTokenStore
JdbcTokenStore
JwtTokenStore
configure(AuthorizationServerSecurityConfigurer) # 使用token服务的安全策略, 授权服务与资源服务分离时配置
接口
Principal /users/current
测试
insert into user(username, password) values('outrun', '$2a$10$l7.7AJEHtXukwUZiKAyVSO6lHJOyHhPxHvi7MHawe8SjlOKkCVbAe')
curl erp-auth-resource:asdf@localhost:9016/uaa/oauth/token -d grant_type=password -d username=outrun -d password=asdf
浏览器
url: localhost:9016/uaa/oauth/token
header
'Authorization': 'Basic ' + base64('erp-auth-resource:asdf')
data
username: 'outrun'
password: '123456'
grant_type: 'password'
资源服务
application.yml # client
security:
oauth2:
resource:
user-info-uri: http://localhost:9016/uaa/users/current
client:
clientId: erp-auth-resource
clientSecret: asdf
accessTokenUri: http://localhost:9016/uaa/oauth/token
grant-type: client_credentials,password
scope: server
注解
@EnableResourceServer # 修饰ResourceServerConfigurerAdapter, 开启资源服务
# 修饰AuthorizationServerConfigurerAdapter, 因为授权服务提供token获取和验证接口
@PreAuthorize("hasAuthority('ROLE_ADMIN')) # 修饰controller方法,验证权限
api
ResourceServerConfigurerAdapter # 资源服务配置
configure(HttpSecurity)
authorizeRequests # 请求放行
测试
curl -d "username=outrun&password=asdf" "localhost:9017/user/registry"
insert into role values(1, 'ROLE_USER'), (2, 'ROLE_ADMIN')
insert into 'user_role' values(user_id, 2)
curl erp-auth-resource:asdf@localhost:9016/uaa/oauth/token -d grant_type=password -d username=outrun -d password=asdf
curl -l -H "Authorization:Bearer 7df6669c-0c86-417b-827f-9a58297f57e5" -X GET "localhost:9017/hello"
客户端
注解
@EnableOAuth2Client # 修饰[Oauth2ClientConfig], 客户端
api
[Oauth2ClientConfig] # 客户端配置, 自定义类,名称任意
ClientCredentialsResourceDetails # bean, 资源信息
RequestInterceptor # bean, 保存请求上下文
OAuth2RestTemplate # bean, 用于向授权服务发送请求
表
clientdetails
oauth_access_token
oauth_approvals
oauth_client_details
oauth_client_token
oauth_code
oauth_refresh_token
spring integration
#
# 服务编排