spring配置文件
<!--一个bean标签对应一个对象-->
<bean id="someService" class="com.bjpowernode.service.impl.SomeServiceImpl"/>
<!--spring可以创建一个非自定义类的对象,存在的某个类-->
<bean id="myDate" class="java.util.Date"/>
public class MyTest {
@Test
public void test(){
//指定spring配置文件名称和位置
String config = "applicationContext.xml";
//创建容器(同时创建配置文件中所有的对象,默认调用无参数构造方法)
ApplicationContext ac = new ClassPathXmlApplicationContext(config);
//获取所有对象个数(常用方法)
int num = ac.getBeanDefinitionCount();
System.out.println(num);
//获取所有对象的名称,返回一个数组(常用方法)
String names []= ac.getBeanDefinitionNames();
for (String name : names){
System.out.println(name);
}
//从spring容器中使用id获取对象
SomeService service = (SomeService)ac.getBean("someService");
service.doSome();
Date date = (Date)ac.getBean("myDate");
System.out.println(date);
}
di依赖注入
注入:赋值
spring中Java的基本数据类型和String都是简单类型
di:给属性赋值
1.set注入(设值注入):
(1)简单注入:spring调用set方法完成属性赋值(类中必须有set方法)(在构造方法之后执行)
执行的是set方法,没有属性有set方法也可执行,如email(Student中没有email属性)
xml规则:所有value都得在""中,与数据类型无关
(2)引用类型的set注入:ref
<bean id="myStudent" class="com.bjpowernode.ba01.Student" >
<property name="name" value="李四"/>
<property name="age" value="20"/>
<property name="email" value="lisi@qq.com"/>
<property name="school" ref="mySchool"/>
</bean>
</bean>
<!--声明School对象-->
<bean id="mySchool" class="com.bjpowernode.ba02.School" >
<property name="name" value="广东工业大学"/>
<property name="address" value="广州"/>
</bean>
2.构造注入:spring调用类的有参数构造方法,在创建对象的同时赋值
(可以使用index索引的方式index=“0“ value=“张飞”)
构造注入创建文件对象(文件类有构造方法)
3.引用类型自动注入:spring框架根据某些规则给引用类型赋值。
(1)byName(按名称注入):java类中引用类型的属性名和spring容器中配置文件的id一样,且数据类型一样,这样容器中的bean,spring能够赋值给引用类型。
( 2)byType(按类型注入):java类中引用类型的数据类型和spring容器中配置文件的class类型是同源关系,这样容器中的bean,spring能够赋值给引用类型。(类型一样,父子类关系,接口和实现类关系)只能符合其中一个同源关系,因为需要bean唯一
<bean id="myStudent" class="com.bjpowernode.ba05.Student" autowire="byType" >
<property name="name" value="张飞"/>
<property name="age" value="25"/>
<!--<constructor-arg name="school" ref="mySchool"/>-->
</bean>
<bean id="mySchool" class="com.bjpowernode.ba05.School" >
<property name="name" value="广东工业大学"/>
<property name="address" value="广州"/>
</bean>
4.多配置文件的使用
一个总的文件包含其他的多个配置文件,一般不定义对象,可以用通配符*
<import resource="classpath:其他配置文件的路径"/>
基于注解的di
需要在类中加入注解,例如@Component
在spring配置文件中添加组件扫描器和加载属性配置文件(有属性配置文件的话)
组件是Java对象,指定注解在项目中的包名
spring会扫描遍历指定的包,把包中所有的注解按照功能创建对象,或给属性赋值
包的指定方式三种:使用多次扫描器,在一个扫描器中用";“或者”,"隔开包,指定父包(不建议)
<context:component-scan base-package="com.bjpowernode.ba01"/>
<context:property-placeholder location="classpath:01.properties"/>
简单类型的属性赋值:@Value("") value是String类型
- 1.在属性定义上面,无需set方法(推荐)
- 2.在set方法上面
引用类型的属性赋值:@Autowired(有个require属性,默认是true(推荐),可以设置为false:找不到引用类型就返回null不报错)
- 1.在属性定义上面,无需set方法(推荐)
- 2.在set方法上面
@Autowired使用的是自动注入原理,支持byName,byType(默认)
byName自动注入比byType在@Autowired下面还需要加多@Qualifier(value=“bean的id”)**
@Component("myStudent")
public class Student {
//使用配置文件,中文会乱码
@Value("${myname}")
private String name;
@Value("22")
private int age;
@Autowired
//@Qualifier("mySchool")
private School school;
}
jdk注解:
引用类型的属性赋值:@Resource:来自jdk中的注解,spring框架提供了对这个注解的功能支持,可以用于引用类型注解,使用自动注入原理,支持byName(默认),byType
- 1.在属性定义上面,无需set方法(推荐)
- 2.在set方法上面
- 先使用byName自动注入,如果赋值失败,则用byType
- 只使用byName:@Resource(name = “bean的id”)
@Component("myStudent")
public class Student {
@Value("张飞")
private String name;
@Value("22")
private int age;
@Resource
//@Resource(name = "mySchool")
private School school;
}
aop面向切面编程
public class MyIncatonHandler implements InvocationHandler {
//目标对象
private Object target;//SomeServiceImpl类
public MyIncatonHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("执行MyIncatonHandler中的invoke()");
//通过动态代理增强功能
Object res = null;
String methodName = method.getName();
if("doSome".equals(methodName)){
ServiceTools.doLog();
res = method.invoke(target,args);//SomeServiceImpl.doSome
ServiceTools.doEnd();
}else{
res = method.invoke(target,args);//SomeServiceImpl.
}
return res;
}
}
public class MyApp {
public static void main(String[] args) {
SomeService target = new SomeServiceImpl();
InvocationHandler handler = new MyIncatonHandler(target);
//使用Proxy创建代理对象
SomeService proxy = (SomeService) Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),handler);
//通过代理执行方法
proxy.doSome();//doSome传给MyIncatonHandler中invoke()的method
System.out.println("============================");
proxy.doOther();//doOther传给MyIncatonHandler中invoke()的method
}
}
aop-aspectJ
需要在spring配置文件声明d对象,声明自动代理生成器
<!-- 声明自动代理生成器:使用aspectj框架内部的功能,创建目标对象的代理对象。
创建代理对象是在内存中实现的,修改目标对象的内存中的结构。
所以目标对象就是被修改后的代理对象
aspectj-autoproxy 会把spring容器中所有的目标对象一次性生成代理对象
-->
<aop:aspectj-autoproxy />
<!-- 添加这个后,IDEA自动在上面帮助添加了约束文件 -->
前置通知
//注解表示当前类是切面类,给业务方法增加功能的类,其中放功能代码
@Aspect
public class MyAspect {
//value完整写法,AspectJ 的切入点表达式(pdf第26页)
//value写错则找不到代理对象,也没有功能增强
@Before(value = "execution(public void com.bjpowernode.ba01.SomeServiceImpl.doSome(String,Integer))")
//value简略写法,AspectJ 的切入点表达式(pdf第26页)
//@Before(value = "execution(* *..SomeServiceImpl.do*(..))")
//JoinPoint可以获取方法的相关信息,必须放在第一个参数位置
public void myBefore(JoinPoint jp){
//获取方法的完整定义和名称
System.out.println("方法的定义="+jp.getSignature());
System.out.println("方法的名称="+jp.getSignature().getName());
//获取方法的实参
Object args[] = jp.getArgs();
for(Object arg:args){
System.out.println("实参:"+arg);
}
//切面功能代码
System.out.println("1---前置通知方法,在目标方法执行之前执行");
}
后置通知
public,方法有参数,推荐是Object,有返回值
在目标方法之后执行,能获取目标方法的返回值
//returning 自定义变量 表示目标方法的返回值 自定义变量名必须和通知方法的形参名一样(下面的两个res)
@AfterReturning(value = "execution(* *..SomeServiceImpl.doOther(..))",returning = "res")
//JoinPoint放在第一个参数,否则报错
public void myAfterReturning(JoinPoint jp,Object res){
System.out.println("方法的定义="+jp.getSignature());
System.out.println("后置通知,在目标方法之后执行,获取的返回值="+res);
//在后置通知中修改res的值是没有用的
}
环绕通知
public,方法有参数,固定的参数ProceedingJoinPoint,必须有一个返回值,推荐Object
环绕通知经常做事务,在目标方法前开启事务,在目标方法后提交事务(事务管理)
功能最强的通知,在目标方法前后都能增强:
- 控制目标方法是否被调用执行
- 修改目标方法的执行结果
等同于invoke(InvocationHandler接口),ProceedingJoinPoint等同于Method
//ProceedingJoinPoint继承JoinPoint,可以直接使用
@Around(value = "execution(* *..SomeServiceImpl.doFirst(..))")
public Object myAround(ProceedingJoinPoint pjp) throws Throwable {
String name = "";
//获取参数值
Object args[] = pjp.getArgs();
if(args!=null && args.length > 1){
Object arg = args[0];
name = (String)arg;
}
//实现环绕通知
Object res = null;
System.out.println("环绕通知,在目标方法之前增强功能");
//目标方法调用
//符合条件则调用目标方法,控制目标方法是否执行
if("zhangsan".equals(name)) {
res = pjp.proceed();//等同于method.invoke();
}
System.out.println("环绕通知,在目标方法之后增强功能");
//在目标方法的前或者后加入功能
//目标方法的执行结果可以修改
if(res!=null){
res="hello";
}
//最后返回值
return res;
}
异常通知(了解)
public,方法参数有一个Exception,如果还有就是JoinPoint,没有返回值
在目标方法抛出异常时执行(做异常的监控程序,用try catch)
//throwing 自定义变量 表示目标方法抛出的异常对象,变量名必须和方法名的参数一样
@AfterThrowing(value = "execution(* *..SomeServiceImpl.doSecond(..))",throwing = "ex")
public void myAfterThrowing(Exception ex){
System.out.println("异常通知:方法发生异常时执行:"+ex.getMessage());
}
最终通知(了解)
public,方法没有参数,如果有就是JoinPoint,没有返回值
总是会执行,在目标方法之后,一般用于资源清除工作
多个value相同复用时可以用@Pointcut,一个辅助功能,用方法名称代替切入点表达式
@Aspect
public class MyAspect {
@After(value = "mypt()")
public void myAfter(){
System.out.println("最终通知:总是会执行的代码");
}
@Before(value = "mypt()")
public void myBefore(){
System.out.println("前置通知");
}
@Pointcut(value = "execution(* *..SomeServiceImpl.doThird(..))")
public void mypt() {
}
}
动态代理
代理对象:由框架创建的一种内存中的对象,通过这个对象执行方法时,可以执行切面的功能代码,实现功能的增强
proxy.getClass().getName()
目标类有接口,默认使用jdk动态代理
目标类没有接口,使用的是cglib动态代理,spring框架自动应用cglib
目标类有接口也可以用cglib动态代理,在spring.xml配置文件中修改:
在aop中加入 proxy-target-class=“true”
spring集成mybatis
使用spring的ioc(控制反转)技术,把mybatis中使用的对象交给spring框架创建,管理,赋值
1.使用连接池
2.创建SqlSessionFactroy对象
3.创建dao代理对象,访问数据库
mybatis.xml配置文件
<!-- settings:控制mybatis全局行为 -->
<settings>
<!-- 配置日志功能 -->
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<!--设置别名-->
<typeAliases>
<!--name:实体类所在包名-->
<package name="com.bjpowernode.domain"/>
</typeAliases>
<mappers>
<!--告诉 mybatis 要执行的 sql 语句的位置-->
<!--name是包名,包中所有的mapper.xml一次都能加载-->
<package name="com.bjpowernode.dao"/>
</mappers>
spring.xml配置文件
声明数据源,用于连接数据库(使用属性配置文件properties)
<context:property-placeholder location="classpath:jdbc.properties"/>
<!--数据库连接池-->
<bean id="myDataSource" class="com.alibaba.druid.pool.DruidDataSource"
init-method="init" destroy-method="clone" >
<!--set注入给DruidDataSource提供连接数据库信息-->
<!--使用属性配置文件中的信息,语法:${key}-->
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
<property name="maxActive" value="20" />
</bean>
创建SqlSessionFactory
<!--声明mybatis中提供的SqlSessionFactoryBean类,这个类内部创建SqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--set注入,把数据库连接池赋给dataSource属性-->
<property name="dataSource" ref="myDataSource" />
<!--mybatis主配置文件的位置,
configLocation属性是Resource类型,读取配置文件赋值使用value,
指定文件路径,使用classpath:表示文件位置
-->
<property name="configLocation" value="classpath:mybatis.xml" />
</bean>
生成每个dao接口的代理对象
<!--创建dao对象,使用SqlSession的getMapper(StudentDao.class)
MapperScannerConfigurer:在内部调用getMapper()生成每个dao接口的代理对象
-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!--指定SqlSessionFactory对象的id-->
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
<!--指定包名,dao接口所在的包
MapperScannerConfigurer会扫描这个包中的所有接口,
每个接口都执行一次getMapper()方法,得到每个接口的dao对象,
创建好的对象放入spring的容器中,dao对象的默认名称是接口名的首字母小写
-->
<property name="basePackage" value="com.bjpowernode.dao" />
</bean>
让service可以用dao
<!--声明service,将上面创建的dao赋给属性,让service可以用dao-->
<bean id="studentService" class="com.bjpowernode.service.impl.StudentServiceImpl">
<property name="studentDao" ref="studentDao"/>
</bean>
使用dao对象
public void testDaoInsert(){
String config = "applicationContext.xml";
ApplicationContext ctx = new ClassPathXmlApplicationContext(config);
//获取spring容器中的dao对象(dao对象的默认名称是接口名的首字母小写studentDao)
StudentDao dao = (StudentDao)ctx.getBean("studentDao");
Student student = new Student();
student.setId(1007);
student.setName("李白");
student.setAge(28);
student.setEmail("libai@qq.com");
int num = dao.insertStudent(student);
System.out.println("num="+num);
}
使用service对象(通过service访问dao)
public void testServiceAdd(){
String config = "applicationContext.xml";
ApplicationContext ctx = new ClassPathXmlApplicationContext(config);
StudentService service = (StudentService)ctx.getBean("studentService");
Student student = new Student();
student.setId(1008);
student.setName("韩信");
student.setAge(27);
student.setEmail("hanxin@qq.com");
int num = service.addStudent(student);
//spring和mybatis整合在一起时,事务自动提交的。无需执行SqlSession.commit();
System.out.println("num="+num);
}
spring的事务处理
需要在sping配置文件声明事务管理器,开启事务管注解驱动
<!--使用spring的事务处理(service的实现类中)-->
<!--1.声明事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--连接数据库,指定数据源-->
<property name="dataSource" ref="myDataSource" />
</bean>
<!--2.开启事务管注解驱动,transaction-manager:事务管理器对象的id-->
<tx:annotation-driven transaction-manager="transactionManager"/>
1.使用service的实现类完成事务处理
使用spring的事务处理,需要放在public上面
@Transactional(
propagation = Propagation.REQUIRED,//传播行为
isolation = Isolation.DEFAULT,//隔离级别
readOnly = false,
rollbackFor = { //发生指定的异常就一定回滚
NullPointerException.class, NotEnoughException.class//自定义异常
}
@Transactional直接这样写则所有属性使用默认值
@Transactiona
@Override
public void buy(Integer goodsId, Integer nums) {
//销售记录
System.out.println("=====buy开始====");
Sale sale = new Sale();
sale.setGid(goodsId);
sale.setNums(nums);
saleDao.insertSale(sale);//insert into sale(gid,nums) values(#{gid},#{nums})
//判断
Goods goods = goodsDao.selectGoods(goodsId);
if (goods == null){
throw new NotEnoughException("编号:"+goodsId+",商品不存在");
}else if (goods.getAmount() < nums){
throw new NotEnoughException("编号:"+goodsId+",商品库存不足");
}
//修改库存
Goods buyGoods = new Goods();
buyGoods.setId(goodsId);
buyGoods.setAmount(nums);
goodsDao.updateGoods(buyGoods);//update goods set amount = amount - #{amount} where id = #{id}
System.out.println("=====buy结束====");
}
2.使用 AspectJ 的 AOP 配置管理事务(不使用@Transactiona)
配置spring.xml配置文件
<!--声明式事务处理,和源代码完全分开-->
<!--1.声明事务管理器对象-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--连接数据库,指定数据源-->
<property name="dataSource" ref="myDataSource" />
</bean>
<!--2.声明业务方法的事务属性(隔离级别,传播行为,超时时间)
id:自定义名称
transaction-manager:事务管理器对象的id
-->
<tx:advice id="myAdvice" transaction-manager="transactionManager">
<!--tx:attributes:配置事务属性-->
<tx:attributes>
<!--tx:method给具体方法配置事务属性,method可以有多个,给不同的方法设置事务属性
name:方法名称,不带包和类,可以使用*
propagation:传播行为,枚举值
isolation:隔离级别
rollback-for:指定异常发生回滚
-->
<!--spring框架中tx:method name先找的是完整名称,再是带有通配符的名称如add*,最后是*-->
<tx:method name="buy" propagation="REQUIRED" isolation="DEFAULT"
rollback-for="com.bjpowernode.excep.NotEnoughException,java.lang.NullPointerException"/>
</tx:attributes>
</tx:advice>
<aop:config>
<!--配置切入点表达式
id:切入点表达式名称
expression:切入点表达式,指定哪些类要用事务,aspectJ会创建代理对象
例如下面写的是所有service类
-->
<aop:pointcut id="servicePt" expression="execution(* *..service..*.*(..))"/>
<!--配置增强器:关联advice和pointcut
advice-ref:tx:advice配置的id
pointcut-ref:切入点表达式的id
-->
<aop:advisor advice-ref="myAdvice" pointcut-ref="servicePt"/>
</aop:config>
spring与web
controller类中
//web中创建spring容器对象
//String config = "spring.xml";
//ApplicationContext ctx = new ClassPathXmlApplicationContext(config);
//spring中直接获取ServletContext中创建好的容器对象来用
//WEB容器在启动时,会为每个WEB应用程序都创建一个ServletContext对象,代表当前web应用
WebApplicationContext ctx = null;
/*String key = WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE;
Object attr = getServletContext().getAttribute(key);
if (attr != null){
ctx = (WebApplicationContext)attr;
}*/
//以上代码可以使用框架中的方法替代,获取对象
ServletContext sc = getServletContext();
ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(sc);
监听器
web.xml配置文件
<!--注册监听器ContextLoaderListener
监听器被创建对象后会读取/WEF-INF/spring.xml(因为需要创建ApplicationContext对象,需要加载配置文件)
/WEF-INF/applicationContext.xml是监听器默认读取的配置文件路径
可以使用context-param重新指定位置
配置监听器目的:创建容器对象,把spring.xml配置文件中所有的对象创建好
-->
<context-param>
<!--contextConfigLocation表示配置文件的路径-->
<param-name>contextConfigLocation</param-name>
<!--自定义配置文件的路径:target/classes/spring.xml-->
<param-value>classpath:spring.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
==================================================================================
spring事务的传播机制及原因分析:
- PROPAGATION_REQUIRED : 支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
- PROPAGATION_SUPPORTS : 支持当前事务,如果当前没有事务,就以非事务方式执行。
- PROPAGATION_MANDATORY : 支持当前事务,如果当前没有事务,就抛出异常。
- PROPAGATION_REQUIRES_NEW : 新建事务,如果当前存在事务,把当前事务挂起。
- PROPAGATION_NOT_SUPPORTED : 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
- PROPAGATION_NEVER : 以非事务方式执行,如果当前存在事务,则抛出异常。
- PROPAGATION_NESTED : 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与PROPAGATION_REQUIRED类似的操作。