注解创建Bean
我们以前创建一个对象是采用XML配置的
<bean id="customer" class="com.lff.domain.Customer" />
采用注解的方式创建,我们首先需要在XML配置文件配置需要扫描的包,能够扫描到所有的@Component注解
<Context:component-scan base-package="com.lff.test" />
如果整个项目都没有XML文件,使用@ComponentScan 注解替代
在需要使用注解的类前面加@Component
@Componentpublic class Person {}
外界使用
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");Person person = (Person) ctx.getBean("person");System.out.println(person);
获取Bean时,默认使用类名的首字母小写的形式做为id当然你也可以使用注解的value属性设置Bean的id。
@Component(value = "person1")public class Person {}
外界获取时,使用person1做为id。
Person person = (Person) ctx.getBean("person1");
在项目中我们需要分层,比如Serlet层、Service层、Dao层。我们对不同的层如果都使用@Component当然没有问题,但不够语义化。
Servlet层我们通常使用@Controller
@Controllerpublic class LoginServlet {}
Service层使用@Service注解
@Service(value = "loginService")public class LoginServiceImpl {}
Dao层使用@Repository注解
@Repository("loginDao")public class LoginDaoImpl {}
当然上面@Controller、@Service、@Repository和@Component功能上并没有区别,都是为了把对象放在IOC容器里面。
一个比较全的示例
@Component(value = "person1")@Scope("singleton") //单例,如果不想为单例值为prototype@Lazy //表示Scope为singleton延时加载@ComponentScan("com.lff.test") //扫描com.lff.test里面的包//@ComponentScans({// @ComponentScan("com.lff.test"),// @ComponentScan("com.lff.send")//})public class Person {}
注解实现注入
当注入的类型为Bean时,使用@Autowired注解
@Component(value = "person")public class Person {private Dog dog;@Autowiredpublic void setDog(Dog dog) {this.dog = dog;}}
@Autowired默认是根据参数的类型进行注入,看参数类型是Dog类型的然后从IOC容器里面找是否有Dog类型的对象。
以下的@Autowired写法都正确
直接放在成员变量上面
@Component(value = "person")public class Person {@Autowiredprivate Dog dog;public void setDog(Dog dog) {this.dog = dog;}}
直接通过反射去找
@Component(value = "person")public class Person {@Autowiredprivate Dog dog;}
也可以使用@Qualifier注解根据id来查找
@Component(value = "person")public class Person {private Dog dog;@Autowired(required = false) //false找不到对应的Bean不会抛出异常,默认为true会抛出异常@Qualifier("dog") //根据id来找public void setDog(Dog dog) {this.dog = dog;}}
当注入的类型为基本类型(String、BigDecimal)时,使用@Value注解
@Component(value = "person")public class Person {private String name;@Value("Cliff")public void setName(String name) {this.name = name;}}
name的名称可以从配置文件里面读取。
@Component(value = "person")@PropertySource("person.properties") //加载配置文件//@PropertySources({// @PropertySource("配置文件1的名称"),// @PropertySource("配置文件2的名称"),// @PropertySource("配置文件3的名称"),//})public class Person {private String name;@Value("${name}") //从配置文件读取namepublic void setName(String name) {this.name = name;}}
注解实现AOP
首先需要在XML文件配置下面的代码代替之前的
<aop:aspectj-autoproxy />
上面的配置其实也可以使用@EnableAspectJAutoProxy注解替代
新建切面类,可以把要拦截的功能都写这个类里面,只需在里面增加方法即可。
@Aspect //标识一个切面类@Component //这个是要加入到IOC容器里面@EnableAspectJAutoProxypublic class PersonAop {@Around("within(com.lff.test..*)") //设置切入点,要求被注解的方法要有返回值,public Object log(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { //方法必须传入一个ProceedingJoinPoint类型的参数,要求的System.out.println("before---------");//调用目标方法Object object = proceedingJoinPoint.proceed();System.out.println("after---------");return object;}}
一般@EnableAspectJAutoProxy注解可以写到@Configuration注解的类中,关于@Configuration注解我们后面会讲到。
外界使用
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");Person person = (Person) ctx.getBean("person");person.run("hahaha");
打印日志
before---------hahahaafter---------
也可以把切入点的代码使用空方法抽取出来
@Aspect //标识一个切面类@Component //这个是要加入到IOC容器里面public class PersonAop {@Pointcut("within(com.lff.test..*)")public void pointCut(){} //抽取切入点@Around("pointCut()") //这里直接调用方法即可public Object log(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { //方法必须传入一个ProceedingJoinPoint类型的参数,要求的System.out.println("before---------");//调用目标方法Object object = proceedingJoinPoint.proceed();System.out.println("after---------");return object;}}
注解替换XML文件
使用@Configuration的注解用于替换applicationContext.xml文件
@Configurationpublic class AppConfig {}
此时AppConfig的类对象会自动放在IOC容器里面。因为@Configuration也是一个@Component注解(用之前也要被扫描到)。
可以在@Configuration修饰的类中使用@Bean注解修饰方法,创建Bean对象。
@Configurationpublic class AppConfig {@Bean// @Bean("person") //设置获取时的id@Scope("prototype")@Lazypublic Person getPerson(){return new Person();}}
默认情况下,方法名就是Bean的id,可以通过 @Bean(“person”)的方式设置id。
被@Bean修饰的方法,他的参数也会被自动注入(当然前提是参数的类型能够在IOC容器里面找到)
当@Bean方法的返回值是一个FactoryBean时,会通过调用FactoryBean的getObject返回生产的对象
public class DogFactoryBean implements FactoryBean<Dog> {@Overridepublic Dog getObject() throws Exception {return new Dog();}@Overridepublic Class<?> getObjectType() {return Dog.class;}}
@Configurationpublic class AppConfig {@Beanpublic DogFactoryBean dog1(){return new DogFactoryBean();}}
外界使用
Dog dog = (Dog) ctx.getBean("dog1");
创建工厂也使用注解
以前我们使用的是XML,创建一个叫applicationContext.xml的文件,然后我们使用如下代码获取工程
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
当然如果XML文件里面的内容过多,我们也可以新建多个XML配置,只需在在applicationContext.xml文件里面导入其他XML文件即可
<import resource="xxx.xml" />
我们能不能不使用XML的方式去创建工程呢?当然可以
使用注解创建工程的方式有很多种:我们只需要关注某个类有没有被扫描到、有没有被导入进来、有没有放到IOC容器里面就可以了,下面是一个具体的示例
@Configuration@ComponentScan("com.lff") //扫描包//@ImportResource("applicationContext.xml") //导入XML,注解和XML混合使用时public class AppConfig {@Beanpublic DogFactoryBean dog1(){return new DogFactoryBean();}}
外界使用
//ApplicationContext ctx = new AnnotationConfigApplicationContext("com.lff"); //传一个包ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class); //传一个包Dog dog = (Dog) ctx.getBean("dog1");System.out.println(dog);
使用AnnotationConfigApplicationContext可以传入一个包名,也可以传入一个类的入口类名获取工厂对象。
Bean的创建方式
Bean创建方式总结起来有3种
- @Component 这种方式一般适用于开发者自己写的类,创建方式比较简单比如直接new
@Bean 这种方式适用于创建方式比较复杂的场景,比如别人写好的类我们直接调用创建Bean
@Beanpublic xxx dog1(){调用其他库的代码return xxx;}
直接在XML配置Bean的方式,这种方式也最为通用,什么类型的对象都能够创建
因为创建Bean有三种方式,当这三种方式同时存并且id也都相同,默认从IOC容器里面取对象的时候是以哪一种方式创建的呢?
从XML里面创建
证明:
@Componentpublic class Person {private String name = "@Component创建";public void setName(String name) {this.name = name;}}
XML配置
<bean id="person" class="com.lff.test.Person"><property name="name" value="XML中的Bean" /></bean>
@Bean方式
@Configuration@ComponentScan("com.lff") //扫描包@ImportResource("applicationContext.xml") //导入XML,注解和XML混合使用时public class AppConfig {@Beanpublic Person person(){Person person = new Person();person.setName("这是使用@Bean方式创建的");return person;}}
外界获取
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class); //传一个包Person person = (Person)ctx.getBean("person");
发现person的name是XML中的Bean,可以证明是先从XML配置创建Bean,把XML注释后发现name是“这是使用@Bean方式创建的”说明其次是从@Bean方式创建的
