spring

Spring

  • 工具
  • 使用
    • 导入相关包
    • 增加配置文件
    • 配置bean
      • 注入方式
      • 引用其他bean
      • 特殊赋值
      • 集合赋值
      • 自动装配
      • 继承
      • 依赖
      • 作用域
      • 配置外部属性文件
      • SpEL
      • bean的生命周期
      • bean的后置处理器
      • 获取bean
        • 通过类全类名基于反射获取
        • 利用静态工厂方法来获取bean
        • 利用实例工厂来创建bean
        • 通过FactoryBean
    • 注解
      • 基本使用
      • 自动装配
      • 泛型依赖注入
      • 示例
  • spring AOP
    • AOP术语
    • 基于注解
      • 对于六种术语的解释
      • 对于五种通知类型的解释
        • Before 前置通知
        • After 后置通知
        • AfterThrowing 异常通知
        • AfterReturning返回通知
        • Around 环绕通知
        • 几种通知的比较
      • AspectJ表达式
      • 切面优先级
      • 重用切点
    • 基于配置文件
  • Spring事务
    • 基于xml
    • 基于注解
    • 注意
    • 事务的传播行为
    • 事务的隔离级别
    • 异常回滚
    • 超时跟只读
  • 整合hibernate
  • 整合struts2

工具

  1. 建议直接下载最新版包含sts的eclipse,可以去https://spring.io/tools

使用

导入相关包

spring_第1张图片

增加配置文件

  1. 在eclipse中直接new一个spring bean configuration file;

配置bean

注入方式

  1. 构造器注入/set注入
public class HelloWorld {

	private String name;
	private String age;
	
	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getAge() {
		return age;
	}

	public void setAge(String age) {
		this.age = age;
	}

	public HelloWorld(String age) {
		super();
		this.age = age;
	}

	@Override
	public String toString() {
		return "HelloWorld [name=" + name + ", age=" + age + "]";
	}


	public static void main(String[] args) {
		ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
		HelloWorld bean = context.getBean("helloWorld",HelloWorld.class);
		System.out.println(bean);
	}
}




	
	


  1. 注意:set注入,name对应bean的属性,value就是属性值;
  2. 注意:构造器注入,name对应参数名,value对应参数值,对应参数列表:参数个数,类别,顺序,其中参数个数是由construct标签的个数决定,参数类别可以由type属性决定,参数顺序可以由index来标记或者由name属性对应参数名称
  3. 注入中有特殊标记,例如“>”这种尖括号,可以使用
    直接使用会报错
    在这里插入图片描述
    使用CDATA标签即可
    spring_第2张图片

引用其他bean

  1. 在标签中使用ref属性即可
    在这里插入图片描述
  2. 使用内部bean,注意内部bean可以不要id,只能内部使用,不能外部引用;
    spring_第3张图片

特殊赋值

  1. 赋值null
    在这里插入图片描述
  2. 级联赋值
    外部bean级联
    在这里插入图片描述
    内部bean级联
    spring_第4张图片
  3. 注意:由内部bean的级联可以,级联的方式是由属性名引用赋值,而并非使用外部或者内部bean的id名称赋值;

集合赋值

  1. list赋值(可以内部bean,也可以引用)

	
		
			
				
			
			
				
			
			
				
			
		
	

  1. map赋值

	


	


	


	
		
			
			
			
		
	

  1. properties赋值

	
		
			root
			123456
			jdbc
			driverclass
		
	

  1. 配置单独的集合bean

	
	
	


	

  1. 利用p命名空间简化配置

自动装配

  1. 在bean的标签中指定autowire属性,spring中id跟name区别,前提是必须生成set方法;
    1. byName:根据bean的名称来自动装配,由于id跟name都只能唯一,故此方法获取bean唯一
    2. byType:根据bean的class类型来进行自动装配

继承

  1. 使用parent让子bean来继承父bean,并且在子bean中还可以覆盖父bean属性



  1. 在父bean中增加abstract属性为true可以让父bean只用来被继承而不能被实例化,若一个bean的class属性没有被指定,则该bean只能是抽象bean

依赖

  1. 使用depends-on属性来指定依赖bean(用逗号分隔多个bean)


  1. 假设car这个bean不存在则启动报错,避免赋空值

作用域

可参考

  1. 通过配置scope来配置
    1. singleton(默认):单例,在初始化spring容器的时候就已经初始化了bean,以后所有的请求都返回相同的bean对象;
    2. prototype:在使用的时候才会创建bean,并且每次使用都会创建新bean,相当于new一个bean

配置外部属性文件

  1. 在spring配置文件中引入context命名空间,并配置location为外部配置文件,然后在使用中使用${}来获取即可;

bean.properties

name=huzd

applicationContext.xml



SpEL

  1. 显示常量:#{‘常量值’},主要括号中需要用单引号或者双引号引起来

  1. 显示静态类的方法或者属性:#{T(方法全类名).method()}

  1. 显示其他bean或者其他bean属性



  1. 使用运算符



bean的生命周期

  1. 调用bean的构造器或者工厂方法创建bean实例对象
  2. 调用bean的set方法根据配置设置属性值
  3. 如果实现了BeanNameAware,BeanFactoryAware等接口,则注入相应的属性
  4. 调用BeanPostProcessor的postProcessBeforeInitialization方法
  5. 调用InitializingBean的afterPropertiesSet方法
  6. 调用自定义的init方法
  7. 调用BeanPostProcessor的postProcessAfterInitialization方法
  8. bean准备就绪,可以使用
  9. 调用DispostbleBean的destory方法
  10. 调用自定义 destroy方法

bean的后置处理器

  1. 实现beanPostProcessor接口
public class MyBeanPostProcessor implements BeanPostProcessor {

	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		System.out.println("before");
		return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
	}

	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		System.out.println("after");
		return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
	}
}
  1. 在配置文件中配置

  1. 最终效果:可以在init前后处理bean
  2. 注意:
    1. 由于BeanPostProcessor是对所有bean都会生效,所以在方法内部需要用beanName来判断具体哪个bean再处理
    2. 在配置文件中如果配置多个实现了BeanPostProcessor接口的类,则会相应的执行多次方法;

获取bean

通过类全类名基于反射获取

  1. 即直接在spring配置文件中配置class属性获取bean

利用静态工厂方法来获取bean

  1. 配置静态工厂类
public class StaticCarFactory {

	private static Map cars = new HashMap();
	
	static {
		cars.put("car", new Car(1000));
	}
	public static Car getCar(String name) {
		return cars.get(name);
	}
}
  1. 配置文件:class指向工厂类,配置的是bean实例,factory-method指向静态工厂方法,如果有参数通过constructor-arg来传入;

	
	

利用实例工厂来创建bean

  1. 创建实例工厂
public class InstanceFactory {

	private Map cars = null;
	
	
	public InstanceFactory() {
		cars = new HashMap();
		
		cars.put("car", new Car(2000));
	}
	
	public Car getCar(String name) {
		return cars.get(name);
	}
	
}
  1. 配置文件:首先需要配置工厂bean实例,然后配置car的实例,配置factory-bean属性指向工厂bean,factory-method指向获取bean的方法,有参数依旧由constructor-arg传入



	

通过FactoryBean

  1. 配置类实现FactoryBean接口
public class CarFactoryBean implements FactoryBean {

	@Override
	public Car getObject() throws Exception {
		return new Car(3000);
	}

	@Override
	public Class getObjectType() {
		return Car.class;
	}
	
	@Override
	public boolean isSingleton() {
		return true;
	}

}
  1. 配置文件直接配置car实例bean并且class指向FactoryBean即可,上面两种都需要指定方法,这个接口配置之后,默认调用实现类的getObject获取car实例

注解

基本使用

  1. 基本的4个注解
    1. Component:组件
    2. Repository:一般用来标记持久层
    3. Service:一般用来标记服务层
    4. Controller:一般用来标记表现层
  2. 配置完成之后需要在配置文件中配置包扫描:base-package用来指定要扫描哪些包,resource-pattern用来匹配特定包或者类
	
  1. 通过默认类名第一个字母小写的方式获取或者使用注解的value属性配置

    如下:配置repository的UserDaoImpl类可以通过userDaoImpl获取

    @Repository
    public class UserDaoImpl implements UserDao {
    
    	@Override
    	public void add() {
    		System.out.println("save");
    	}
    
    }
    
    public class Test {
    
    	public static void main(String[] args) {
    		ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext3.xml");
    		
    		UserDaoImpl userDaoImpl = context.getBean("userDaoImpl",UserDaoImpl.class);
    		
    		userDaoImpl.add();
    	}
    }
    
  2. 或者通过使用value属性来配置

    @Repository(value = "userDao")
    public class UserDaoImpl implements UserDao {
    
    	@Override
    	public void add() {
    		System.out.println("save");
    	}
    
    }
    
    public class Test {
    
    public static void main(String[] args) {
    	ClassPathXmlApplicationContext context = new 						ClassPathXmlApplicationContext("applicationContext3.xml");
    	
    	UserDaoImpl userDaoImpl = context.getBean("userDao",UserDaoImpl.class);
    	
    	userDaoImpl.add();
    	}
    }
    
  3. context:component-scan内部配置子标签context:exclude-filter

    1. annotation类型排除:表示排除所有以Repository这个注解的bean
    		
    
    1. annotation类型包含:表示只包含Repository这个注解的bean,需要注意的是,需要在父标签context:component中配置use-default-filters="false",可以理解成,默认配置为true是扫描用的默认过滤器扫描包下所有文件,配置context:exclude-filter标签会从所有文件中过滤掉某些文件(交集),但是context:include-filter会扫描某些文件(并集),最终得到的跟还是所有文件都扫描,所以需要关掉默认过滤器才有效果;
    
    	
    
    
    1. 接口类型排除:设置type为assignable,expression为某一接口类,则通过该接口实现的类都排除
    		
    
    
    1. 接口类型包括:设置type为assignable,expression为某一接口类,则通过改接口实现的类都可以获取到
    	
    	
    
    

自动装配

  1. 在构造器,字段,方法上面都可以使用@Autowired来注入相应值(构造器跟方法注入的是会注入相同参数名称类型的bean)
  2. 默认情况下Autowired如果找不到装配的类会报错,可以摄者required属性为false不报错(为null)
  3. 当有过个兼容类型的bean是,自动装配会报错,一种解决方案是将需要注入的类设置成跟注入的属性名称一致(autowire会先byType再byName,直到找到唯一一个),另外一种方式是使用@Qualifier属性将bean微调注入

UserServiceImpl类

@Service
public class UserServiceImpl implements UserService {

	UserDao UserDao;
	@Override
	public void add() {
		
		UserDao.add();
	}

}

UserServiceImpl2

@Service
public class UserServiceImpl2 implements UserService {

	UserDao UserDao;
	@Override
	public void add() {
		
		UserDao.add();
	}

}

直接装配时,先byType会找到两个userService接口的类,再byName还是会找到userServiceImpl跟userServiceImpl2两个类(默认以类名第一个字母小写作为id),这样的话会报错,可以使用Qualifier注解将userServiceImpl微调成userService注入

@Controller
public class UserController {

	@Autowired
	@Qualifier("userServiceImpl")
	UserService userService;
	
	public void execute() {
		userService.add();
	}
	
}

使用在方法中就是参数前微调

@Controller
public class UserController {

	UserService userService;
	
	@Autowired
	public void setUserService(@Qualifier("userServiceImpl") UserService userService) {
		this.userService = userService;
	}
	
	public void execute() {
		userService.add();
	}
	
}
  1. @Autowired也可以应用在数组,集合,map上面,放在数组上面就是匹配所有数组类型的类注入,放在集合上面就是匹配所有的集合类型的类注入,放在map上面,若map的键为String,则是以bean的id为key,bean本身作为value注入;

泛型依赖注入

  1. 一个泛型父类引用另外一个泛型父类,那么子类也会自动将同一个泛型对应依赖,但是如果有多个类型匹配则会报错;

示例

父类service泛型类引用BaseRepository泛型父类

public class BaseService {

	@Autowired
	protected BaseRepository repository;
	
	public void add() {
		System.out.println("add");
		System.out.println(repository);
	}
}

父类BaseRepository泛型类

public class BaseRepository {

}

具体UserService 类继承BaseService并设置泛型为User

@Service
public class UserService extends BaseService {

}

具体UserRepository 继承BaseRepository并设置泛型为User,此时UserService 就自动依赖上了UserRepository

@Repository
public class UserRepository extends BaseRepository {

}

若还有另外一个也是设置User泛型,则会报错;

@Repository
public class UserRepository2 extends BaseRepository{

}

spring AOP

AOP术语

  1. 切面(aspect):需要插入的对象,例如日志切面;
  2. 通知(advice):切面必须要完成的工作;
  3. 目标(target):被通知的对象
  4. 代理(proxy):向目标对象应用通知之后创建的对象
  5. 连接点(joinPoint):程序执行的某个特定位置,由两个要素决定,方法跟方位(方法执行之前,之后,异常后等等)
  6. 切点(pointCut):切面与业务逻辑相交的点,对应连接点根据方位不同,一个切点对应多个连接点

基于注解

  1. 在配置文件中增加配置
	 
	 
  1. UserService中引用Userdao
@Service
public class UserService {

	@Autowired
	private UserDao userDao;
	
	public void save(User u) {
		System.out.println("save user");
		userDao.add(u);
	}
	
	public void add(User u) {
		System.out.println("add User....");
	}
}
@Repository
public class UserDaoImpl implements UserDao {

	@Override
	public void add(User u) {
		System.out.println("add User....");
	}

}
  1. 新建切面,并设置before注解表示在某个方法之前执行,由org.aspectj.lang.JoinPoint可以获取方法信息
@Aspect
@Component
public class LoggingAspect {

	@Before("execution(public void com.huzd.study01.aop.UserService.*(User))")
	public void beforeAdd(JoinPoint joinpoint) {
		String methodName = joinpoint.getSignature().getName();
		Object[] args = joinpoint.getArgs();
		System.out.println("methodName:"+methodName);
		System.out.println("args:"+Arrays.asList(args));
		System.out.println("before add...");
	}
}

对于六种术语的解释

  1. 切面:在AspectJ注解中,切面是一个带有@Aspect注解的java类
  2. 通知:AspectJ支持五种类型的通知:
    1. @Before:前置通知,在方法执行之前执行
    2. @After:后置通知,在方法执行之后执行
    3. @AfterReturning:返回通知,在方法返回结果之后执行
    4. @AfterThrowing:异常通知,在方法返回异常之后执行
    5. @Around:环绕通知,围绕着方法执行
  3. 目标:例如上面UserService类
  4. 代理:AOP容器生成的代理对象(在配置文件以标签声明)
  5. 连接点:切面中的方法
  6. 切点:目标中的方法

对于五种通知类型的解释

Before 前置通知

在UserService的save方法执行之前执行beforeAdd方法

	@Before("execution(public void com.huzd.study01.aop.UserService.save(User))")
	public void beforeAdd(JoinPoint joinpoint) {
		String methodName = joinpoint.getSignature().getName();
		Object[] args = joinpoint.getArgs();
		System.out.println("methodName:"+methodName);
		System.out.println("args:"+Arrays.asList(args));
		System.out.println("before add...");
	}

After 后置通知

在UserService的save方法执行之后执行afterAdd方法

@Aspect
@Component
public class LoggingAspect {

	@After("execution(public void com.huzd.study01.aop.UserService.save(User))")
	public void afterAdd(JoinPoint joinpoint) {
		String methodName = joinpoint.getSignature().getName();
		Object[] args = joinpoint.getArgs();
		System.out.println("methodName:"+methodName);
		System.out.println("args:"+Arrays.asList(args));
		System.out.println("after add...");
	}
}

AfterThrowing 异常通知

在UserService的save方法抛出异常之后执行,需要注意的是,如果异常被捕获,则不会执行;

@Aspect
@Component
public class LoggingAspect {

	@AfterThrowing("execution(public void com.huzd.study01.aop.UserService.save(User))")
	public void beforeAdd(JoinPoint joinpoint) {
		String methodName = joinpoint.getSignature().getName();
		Object[] args = joinpoint.getArgs();
		System.out.println("methodName:"+methodName);
		System.out.println("args:"+Arrays.asList(args));
		System.out.println("after add...");
	}
}

AfterReturning返回通知

无论连接点是正常返回还是异常返回,后置通知都会执行,如果指向让正常返回才执行,可以用返回通知代替后置通知,在注解中通过设置returning = "result"还可以获取返回值

@Aspect
@Component
public class LoggingAspect {

	//@AfterThrowing("execution(public void com.huzd.study01.aop.UserService.save(User))")
	//@After("execution(public void com.huzd.study01.aop.UserService.save(User))")
	@AfterReturning(value = "execution(public String com.huzd.study01.aop.UserService.save(User))",returning = "result")
	public void afterAdd(JoinPoint joinpoint,Object result) {
		String methodName = joinpoint.getSignature().getName();
		Object[] args = joinpoint.getArgs();
		System.out.println("methodName:"+methodName);
		System.out.println("args:"+Arrays.asList(args));
		System.out.println("after add...");
		System.out.println("result:"+result);
	}
}

Around 环绕通知

其实就是类似动态代理,连接点必须有ProceedingJoinPoint 参数,并且需要调用proeed方法

	@Around(value = "execution(public String com.huzd.study01.aop.UserService.save(User))")
	public Object around(ProceedingJoinPoint pjd) throws Throwable {
		Object proceed = null;
		try {
			System.out.println("前置通知");
			proceed = pjd.proceed();
			System.out.println(1/0);
			System.out.println("返回通知");
		} catch (Exception e) {
			System.out.println("异常通知");
		}
		System.out.println("后置通知");
		return proceed;
	}

几种通知的比较

因为AOP是利用的动态代理来实现,因此几种通知相当于在invoke中如下位置:

	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		// TODO Auto-generated method stub
		Object result = null;
		try {
			//前置通知
			result = method.invoke(userDao, args);
			//返回通知
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			//异常通知
		}
		//后置通知
		return result;
	}

AspectJ表达式

execution(* *.*.*(. .))
第一个*表示任意修饰符跟任意返回值
第二个*表示任意包
第三个*表示任意类
第四个*表示任意方法
最后括号表示方法,中间的..表示任意参数

  1. 指向com.huzd.study01.aop这个包下的UserService这个类的具有public修饰符并且返回值为void,有一个User类型参数的save方法
execution(public void com.huzd.study01.aop.UserService.save(User))
  1. 指向com.huzd.study01.aop这个包下的UserService这个类的具有public修饰符并且返回值为void,有任意参数的save方法
execution(public void com.huzd.study01.aop.UserService.save(..))
  1. 指向com.huzd.study01.aop这个包下的UserService这个类的具有public修饰符并且返回值为void,有任意参数的任意方法
execution(public void com.huzd.study01.aop.UserService.*(..))
  1. 指向com.huzd.study01.aop这个包下的任意类的具有public修饰符并且返回值为void,有任意参数的任意方法
execution(public void com.huzd.study01.aop.*.*(..))
  1. 指向com.huzd.study01.aop这个包下的任意类的具有任意修饰符并且返回值任意类型,有任意参数的任意方法
execution(* com.huzd.study01.aop.*.*(..))
  1. 指向任意包下的任意类的具有任意修饰符并且返回值任意类型,有任意参数的任意方法
execution(* *.*.*(..))
  1. 指向任意包下的任意类的具有任意修饰符并且返回值任意类型第一个参数为Int类型,后面为任意类型或数量参数的任意方法
execution(* *.*.*(Int,..))
  1. 符合前面表达式或者符合后面表达式的方法(&&,!同理)
execution(* *.*.*(..)) || execution(* *.*.*(..))

切面优先级

在class上实现Ordered接口或者使用@Order注解,getOrder()返回值或者是@Order的值越小则优先级越高;

重用切点

在切面中声明通知方法时,一个切点表达式可能会出现在多个通知中,使用@PointCut来将一个切点声明为普通方法,该方法通常是空的,因为将切点的定义与逻辑混淆是不合理的;

将切点的定义定义成pointCut普通方法,方法可以由pointCut属性指定也可以由value属性指定,如果切点定义不在本类中,需要指定包名跟类名,例如com.huzd.study01.aop.LoggingAspect.getPointCut()

@Aspect
@Component
public class LoggingAspect {

	//@AfterThrowing("execution(public void com.huzd.study01.aop.UserService.save(User))")
	//@After("execution(public void com.huzd.study01.aop.UserService.save(User))")
	@AfterReturning( pointcut = "com.huzd.study01.aop.LoggingAspect.getPointCut()",returning = "result")
	public void afterAdd(JoinPoint joinpoint,Object result) {
		String methodName = joinpoint.getSignature().getName();
		Object[] args = joinpoint.getArgs();
		System.out.println("methodName:"+methodName);
		System.out.println("args:"+Arrays.asList(args));
		System.out.println("after add...");
		System.out.println("result:"+result);
	}
	
	//@Around("getPointCut()")
	public Object around(ProceedingJoinPoint pjd) throws Throwable {
		Object proceed = null;
		try {
			System.out.println("前置通知");
			proceed = pjd.proceed();
			System.out.println(1/0);
			System.out.println("返回通知");
		} catch (Exception e) {
			System.out.println("异常通知");
		}
		System.out.println("后置通知");
		return proceed;
	}
	
	@Pointcut(value = "execution(public String com.huzd.study01.aop.UserService.save(User))")
	public void getPointCut() {}
}

基于配置文件




	
	 
	 
	 
	 
	 
	 
	 
	 	
	 	
	 	
	 	
	 		
	 		
	 	
	 
	

Spring事务

声明式事务有两种,基于xml跟基于注解

基于xml

  1. 首先构建数据源dataSource
  2. 然后声明事务管理器
  3. 声明事务通知
  4. 声明aop配置
    spring_第5张图片

基于注解

  1. 声明事务管理器
  2. 注解扫描包
  3. 使事务注解生效
  4. 然后再在需要添加事务的方法上增加@Transactional
    spring_第6张图片

注意

由于springAOP是基于代理的方法,所以只能增强公共方法,因此只有公共方法才能通过springAOP进行事务管理

事务的传播行为

基于注解:@Transactional(propagation=XXX)
基于xml:
spring_第7张图片

  1. required(默认):如果外层方法有事务,那么内层方法直接使用外层方法的事务,因此,只要整个外层事务中有一个方法调用失败,那么整个外层事务都回滚了;
    spring_第8张图片
  2. required_new:外层方法有一个事务,内层方法自己独立开启事务,因内层方法事务互不影响,外层方法中若有一个事务回滚,只对其自己的事务生效;
    spring_第9张图片
    spring_第10张图片

事务的隔离级别

基于注解
在这里插入图片描述
基于xml
spring_第11张图片
spring_第12张图片

异常回滚

默认情况下,只有未检查异常会导致事务回滚,而受检查的不会,因此受检查异常需要自己捕获并重新抛出运行时异常,也可以使用rollbackFor或者noRollbackFor属性来定义
rollbackFor:遇到异常回滚
noRollbackFor:遇到异常不会滚;
spring_第13张图片

超时跟只读

  1. 超时:事务在强制回滚之前,事务的占用时间;这样可以防止事务长时间占用资源
  2. 只读:事务只是读取数据不会修改事务,帮助数据库引擎优化事务
    spring_第14张图片

整合hibernate

整合struts2

你可能感兴趣的:(java,spring)