相关文章:Spring学习笔记-AOP
慢慢接触spring框架,将所学所用的东西记录下来,点点滴滴,肯定会有所收获。
之所以使用Spring框架,是因为框架可以大量简化程序的开发工作,更加高效稳定的开发大型程序。
IoC(Inversion of Control)控制反转,就是在一般情况下,获取类的实例必须要在程序中new这个类,现在不需要这样了,直接通过配置xml文件,或者对类进行注解,在一个叫做IOC容器的东西里,就存在了类的实例,我们称之为bean,通过在IOC即可获取到要得到的实例。
在Eclipse上使用Spring框架,首先要安装spring在Eclipse上的插件springsource-tool-suite-3.7.1.RELEASE,为了Eclipse上使用spring。
下载下来后,在安装软件里安装好。
下载spring的jar包spring-framework-4.2.2.RELEASE-dist.zip,里边lib里有使用spring时的jar包。
下载commons-logging包,spring的日志系统所依赖。
将这些5个jar包配置在项目中:
创建一个HelloWorld类:
package com.cqupt.spring.beans;
public class Helloworld {
private String name;
public void setName(String name){
this.name=name;
}
public void hello(){
System.out.println("hello :"+name);
}
}
***配置xml文件,在src下新建spring Bean Configration File 文件,在beans标签下新建bean标签
注意,使用property配置属性,类中必须要有无参构造函数,因为原理是先使用无参构造函数构造类对象,然后使用set方法配置属性
id代表这个bean的唯一标示,class是映射到的类,property是类里的成员属性配置,name的识别是根据setter函数识别的,如setName2(String ),那么name="name2",value就是给成员属性配置的值。
在main函数中使用spring
public static void main(String[] args){
//1、创建spring的IOC容器
ApplicationContext ac= new ClassPathXmlApplicationContext("applicationContext.xml");
//2、从IOC容器中获取Bean实例
Helloworld helloworld = (Helloworld) ac.getBean("helloworld");
helloworld.hello();
}
注:在执行
ApplicationContext ac= new ClassPathXmlApplicationContext("applicationContext.xml");
这条语句时,其实已将创建了Helloworld的实例,也调用了Set方法。
getBean有两种方式:根据xml中的id,一种是根据Helloworld.class,区别就是两个bean标签映射到同一类时,getBean(类.class)会出错。
Spring提供两种类型的IOC容器,BeanFactory和ApplicationContext,其中ApplicationContext是对于使用Spring的开发人员的,BeanFactory是Spring的底层,不经常使用。
ApplicationContext是一个接口,有两种IOC的实现类,ClassPathXmlApplicationContext(依据类路径)和FileSystemXmlApplicationContext(依据系统文件路径);
三种注入方式:属性注入,构造器注入,工厂方法注入(不推荐,很少用)
属性注入:依据Set方法,最常用的方式;
构造方法注入:constructor-arg标签
创建一个Car类,构造函数
package com.cqupt.spring.beans;
public class Car {
private String brand;
private String corp;
private int price;
private int maxSpeed;
public Car(String brand, String corp, int price) {
super();
this.brand = brand;
this.corp = corp;
this.price = price;
}
@Override
public String toString() {
return "Car [brand=" + brand + ", corp=" + corp + ", price=" + price
+ ", maxSpeed=" + maxSpeed + "]";
}
}
xml配置
index可以不需要,但是写出来时,一定要注意与第几个value值的匹配,否则出错
main函数中使用:
public static void main(String[] args){
//1、创建spring的IOC容器
ApplicationContext ac= new ClassPathXmlApplicationContext("applicationContext.xml");
Car car = (Car) ac.getBean("car");
System.out.println(car);
}
注:当出现重载构造函数时,可用type属性进行区分,一个构造函数对应一个bean标签,一个参数对应一个constructor-arg标签
]]>
出错
在property标签下,使用list、set、map标签
Person类有集合属性cars
public List getCars(){
return cars;
}
public void setCars(List cars){
this.cars=cars;
}
private String name;
private int age;
private List cars;
xml配置
也可以配置内部bean
使用
Person类
public Map getCars(){
return cars;
}
public void setCars(Map cars){
this.cars=cars;
}
private String name;
private int age;
private Map cars;
Xml
在property标签下,使用
DataSource类,含有Properties属性
public class DataSource {
public Properties getProperties() {
return properties;
}
public void setProperties(Properties properties) {
this.properties = properties;
}
private Properties properties;
}
lihua
123456
www.baidu.com
单独配置一个bean集合,供不同的property使用,需在xml中引入util命名空间
先导入命名空间p,可以简化配置bean属性,bean引用注意,使用cars-ref=“beanid”
自动装配,是通过在bean标签里设置autowire属性,让系统根据名称还是类型进行自动装配。
autowire="byName"是根据引用的bean的名称与类型中的属性名称进行匹配
autowire="byType"是根据引用的bean的类型与类型中的属性类型进行装配,当多个bean指向同类型时,不能根据类型装配,否则异常。、
新建Person类,有Address和Car两个属性,Address和Car均为自定义类型
xml中的配置,先配置address和car的bean,再在person中自动装配这两个bean:
public static void main(String[] args){
ApplicationContext ac=new ClassPathXmlApplicationContext("newApplicationContext.xml");
Person person = (Person)ac.getBean("person");
System.out.println(person);
}
注意,autowire装配是有缺点的:1,只要指定autowire,那么所有引用bean都设置成自动装配;2、要么是byName,要么是byType,不够灵活
一般,开发过程中,不使用自动装配。在整合第三方框架时使用。
父bean可以使实例化bean,也可以是抽象bean,用作模板使用,bean标签设置属性abstract="true",此时父bean 不能实例化,否则出错
public static void main(String[] args) {
ApplicationContext ctx= new ClassPathXmlApplicationContext("applicationContext-scope.xml");
Car car= (Car) ctx.getBean("car");
Car car2= (Car)ctx.getBean("car");
System.out.println(car==car2);
}
输出结果为true
System.out.println("Car Constructing...");
public static void main(String[] args) {
ApplicationContext ctx= new ClassPathXmlApplicationContext("applicationContext-scope.xml");
}
此时输出结果为:Car Constructing...
public static void main(String[] args) {
ApplicationContext ctx= new ClassPathXmlApplicationContext("applicationContext-scope.xml");
Car car= (Car) ctx.getBean("car");
Car car2= (Car)ctx.getBean("car");
System.out.println(car==car2);
}
user=root
password=root
driverClass=com.mysql.jdbc.Driver
jdbcUrl=jdbc:mysql:///test
spring配置文件中配置bean,在spring2.5后使用引入命名空间context的方式加载properties文件:
程序
public static void main(String[] args) throws SQLException {
ApplicationContext ctx= new ClassPathXmlApplicationContext("properties.xml");
DataSource dataSource= (DataSource) ctx.getBean("dataSource");
System.out.println(dataSource.getConnection());
}
输出结果为com.mchange.v2.c3p0.impl.NewProxyConnection@17776a8 [wrapping: com.mysql.jdbc.JDBC4Connection@69a10787]
public static void main(String[] args){
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext-spel.xml");
Address address = (Address) ctx.getBean("address");
System.out.println(address);
Car car = (Car) ctx.getBean("car");
System.out.println(car);
Person person = (Person) ctx.getBean("person");
System.out.println(person);
}
public class Car {
private String brand;
public Car(){
System.out.println("Car Constructor...");
}
public void setBrand(String brand){
System.out.println("Car setter()...");
this.brand = brand;
}
public void init2(){
System.out.println("Car init()...");
}
public void destroy(){
System.out.println("Car destroy()...");
}
}
xml配置
主方法:
public static void main(String[] args){
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("cycle.xml");
Car car = (Car) ctx.getBean("car");
System.out.println("使用过程 println:"+car);
ctx.close();
}
输出结果:
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessAfterInitialization(Object arg0, String arg1)
throws BeansException {
System.out.println("postProessAfterInitialization... "+arg0+" "+arg1);
return arg0;
}
@Override
public Object postProcessBeforeInitialization(Object arg0, String arg1)
throws BeansException {
System.out.println("postProessBeforeInitialization... "+arg0+" "+arg1);
return arg0;
}
}
Object arg0 :就是传入进的bean实体
无需指定id,IOC容器会自动加载该后置处理器的bean,不过指定了也不会出错,其实没必要指定
public static void main(String[] args){
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("cycle.xml");
Car car = (Car) ctx.getBean("car");
System.out.println("使用过程 println:"+car);
ctx.close();
}
输出结果:
配置bean,我们前边一直是用的bean对应的全类名,即bean标签的属性class=“全类名”。现在使用工厂方法配置bean,分为两种:静态工厂方法、实例工厂方法。
通过定义一个静态工厂,在配置bean时,让class="静态工厂全类名",和factory-method来配置bean
定义一个Car的静态工厂:
public class StaticFactory {
private static Map cars= new HashMap();
static{
cars.put("audi", new Car("audi",300000));
cars.put("Boma", new Car("Boma",400000));
}
public static Car getCar(String brand){
return cars.get(brand);
}
}
利用这个静态工厂的静态方法getCar配置bean标签的factory-method属性
使用constructor-arg子节点为静态工厂方法进行传参。
创建非静态工厂:
public class InstanceFactory {
private Map cars;
public InstanceFactory(){
cars = new HashMap();
cars.put("Ford", new Car("Ford",200000));
cars.put("Boma", new Car("Boma",200000));
}
public Car getCar(String brand){
return cars.get(brand);
}
}
配置工厂方法的bean,以及Car的bean
使用factory-bean将工厂的bean引用,factory-method指定方法,constructor-arg传参
目前配置bean已经详述了 全类名、工厂方法两种方式,还有一种就是利用FactoryBean配置
FactoryBean是一个接口,继承此接口的类,可以用来配置bean
创建一个继承此接口的类:CarFactoryBean
public class CarFactoryBean implements FactoryBean {
String brand;
public void setBrand(String brand){
this.brand = brand;
}
@Override
public Car getObject() throws Exception {
return new Car("BMW",300000);
}
@Override
public Class getObjectType() {
return Car.class;
}
@Override
public boolean isSingleton() {
return true;
}
}
继承FactoryBean接口的类都会从接口中继承三个方法:getObject 返回bean原型,getObjectType 返回bean类型,isSingleton 限定是否单利模式。
bean配置,class指向继承接口的类,property子节点时间上是配置的CarFactoryBean的属性,该bean返回的是FactoryBean的getObject方法返回的实例。
目前只是熟悉了工厂方法,FactoryBean 配置bean,但这两种方式的功效具体在哪些方面实现,有待于深入学习,现在是觉得不如全类名配置简单。
前边讲的三种配置bean的方式:全类名,工厂方法,FactoryBean都是基于xml文件的方式,基于注解的方式配置bean也是一种常用的方式。
基于注解的方法就是在程序中,在类或者类属性的前边通过添加@形式的注解,spring会依据配置自动扫描这些添加了注解的类进入IOC容器中,IOC中的bean的默认名字就是类名第一个字母小写。
@对类进行注解有四种形式:Component,Repository,Controller,Service,这四种的区别有待于研究。
建立四个包:其他三个包是第一个的子包
com.cqupt.spring.beans.annotation
com.cqupt.spring.beans.annotation.repository
com.cqupt.spring.beans.annotation.service
com.cqupt.spring.beans.annotation.controller
在annotation包下建立一个TestObject类,并通过@Component对类进行注解
@Component
public class TestObject {
}
在repository包中建立UserRepository接口,注意,接口是不能进行注解的,因为接口没必要配置bean
public interface UserRepository {
public void save();
}
在建立这个接口的实现类UserRepositoryImpl,对此接口进行注解,@Repository
@Repository(value="userRepository")
public class UserRepositoryImpl implements UserRepository {
public void save(){
System.out.println("UserRepository save...");
}
}
如果bean不使用默认的第一字母小写的类名,可以用上述形式指定名称,也可以去掉value。
同样在service包中创建UserService类,@Service注解
@Service
public class UserService {
public void add(){
System.out.println("UserService add...");
}
}
在controller中创建UserController包,@Controller注解
@Controller
public class UserController {
public void execute(){
System.out.println("UserController execute...");
}
}
当类建立完成并且注解之后,需要在xml中配置,使得spring依据这个配置检测注解的类,并在IOC中生成bean。
使用context:component-scan标签,base-package是指定检测的包及其子包,所以四个包都被IOC进行检测
主函数:
public static void main(String[] args) {
ApplicationContext ctx= new ClassPathXmlApplicationContext("beans-annotation.xml");
TestObject to= (TestObject) ctx.getBean("testObject");
System.out.println(to);
UserService userService = (UserService) ctx.getBean("userService");
System.out.println(userService);
userService.add();
UserController userController = (UserController) ctx.getBean("userController");
System.out.println(userController);
userController.execute();
UserRepository userRepository = (UserRepository) ctx.getBean("userRepository");
System.out.println(userRepository);
userRepository.save();
}
输出结果:
com.cqupt.spring.annotation.TestObject@727803de
com.cqupt.spring.annotation.service.UserService@704921a5
UserService add...
com.cqupt.spring.annotation.controller.UserController@df27fae
UserController execute...
com.cqupt.spring.annotation.repository.UserRepositoryImpl@24a35978
UserRepository save...
另外:context:component-scan 标签的resource-pattern="repository/*.class"属性指定了IOC检测类的范围限定在repository包下。
此时在Main中只能获取到UserRepository的对象,因为IOC只创建了名为userRespository的bean
通过注解机制,可以使IOC检测程序中被注解的类生产bean,但是如果不希望所以注解的类均生成bean,该如何呢?
使用范围限制:两种:一是使用resource-pattern的属性,另一种就是在context:component-scan 标签子节点context:exclude-filter排除和context:include-filter包含。
context:include-filter经常和use-default-filters="false"结合使用:表示只包含之意。
context:exclude-filter排除和context:include-filter包含这两个节点依据属性type="annotation"和type="assignable“分为两种类型:依据注解组件类型,和依据类的类型
依据组件类型的排除:
这样,IOC容器中不会有被Repository注解的bean,注意expression="全类名"
依据类类型的排除:
所有继承接口UserRepository的类均不会在IOC容器中生成bean
依据类类型的包含(只包含)
在IOC中只有继承接口UserRepository的类才会生成bean
当然上述这些范围限定都是在类被注解的基础上的,如果类没有被注解,无论怎么包含,是annotation还是assignable,都不会生成bean
在使用基于注解配置bean时,会使用context:component-scan标签,这个标签还会自动注册一个bean后置处理器AutowiredAnnotationBeanPostProcessor,来实现自动装配bean的属性
使用注解@Autowired @Resource @Inject,最常用的是@Autowired注解
详述@Autowired使用:
使用位置,可以放在普通字段、构造器、一切具有参数的方法前
放在普通字段前:
@Controller
public class UserController {
@Autowired
UserService userService;
public void execute(){
System.out.println("UserController execute...");
userService.add();
}
}
放在方法前:
@Controller
public class UserController {
UserService userService2;
@Autowired
public void setUserService2(UserService userService2) {
this.userService2 = userService2;
}
public void execute(){
System.out.println("UserController execute...");
userService2.add();
}
}
@Autowired是按照兼容类型自动装配的,如果IOC中已经有生成了类型UserService的bean,不管名字是userService,还是userService2,都可以自动装配进来。
如果IOC中没有生成UserService的bean,那么程序会抛出异常。解决方法是指定@Autowired的属性required为false, @Autowired(required=false)
既然@Autowired是根据类型自动装配的,那么如果IOC中有两个以上同类型的bean的,只是bean名称不一样,就需要注解@Qualifier("beanid"),根据bean名称指定自动装配的bean,实际上在这种情况下,不指定@Qualifier,系统会自动选择名字与属性名一样的bean
@Service
public class UserService {
@Autowired
UserRepository userRepository;
public void add(){
System.out.println("UserService add...");
userRepository.save();
}
}
有两个UserRepository的bean
@Repository("userRepository")
public class UserRepositoryImpl implements UserRepository {
public void save(){
System.out.println("UserRepository save...");
}
}
第一个指定bean名称为userRepository
@Repository
public class UserJdbcRepository implements UserRepository {
@Override
public void save() {
System.out.println("UserJdbcRepository save...");
}
}
第二个没有指定,就是默认的userJdbcRepository
此时@Autowired注解的属性将默认选择名为与属性名相同的bean,即userRepository
如果第一个bean没有指定名字为userRepository
@Repository
public class UserRepositoryImpl implements UserRepository {
public void save(){
System.out.println("UserRepository save...");
}
}
那么ICO中两个bean分别是userRepositoryImpl和userJdbcRepository
就会抛出异常:Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [com.cqupt.spring.annotation.repository.UserRepository] is defined: expected single matching bean but found 2: userJdbcRepository,userRepositoryImpl
此时就必学添加@Qualifier注解:
@Service
public class UserService {
@Autowired
@Qualifier("userJdbcRepository")
UserRepository userRepository;
public void add(){
System.out.println("UserService add...");
userRepository.save();
}
}
注意:也经常把@Qualifier放在方法的中:
UserRepository userRepository;
@Autowired
public void setUserRepository(@Qualifier("userJdbcRepository") UserRepository userRepository) {
this.userRepository = userRepository;
}
泛型依赖注入与普通注入的区别在于,使用了泛型,在父类之间形成注入依赖,被子类继承,子类可以自动进行依赖注入
创建两个泛型父类:BaseRepository,BaseService,BaseService的属性与BaseRepository形成依赖注入关系
public class BaseRepository {
}
public class BaseService {
@Autowired
protected BaseRepository repository;
public void add(){
System.out.println("Service add...");
System.out.println(repository);
}
}
创建两个父类的子类UserRepository和UserService,并注解给IOC
@Repository
public class UserRepository extends BaseRepository {
}
@Service
public class UserService extends BaseService {
}
通过测试:
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans-module.xml");
UserService us=(UserService) ctx.getBean("userService");
us.add();
}
Service add...
com.cqupt.spring.generic.di.UserRepository@5d11346a
注:注解不可以继承