单例Bean:不是指在Spring容器中只有一个userService类型的对象,可以有多个此类型的对象。Spring找到对应的对象是先 byType 再 byName。
通过@Bean标签就可以看出来Spring中的单例Bean并不是指只有一个此类型的对象。
单例模式:单例类只能有一个实例,单例类必须自己创建自己的唯一实例,单例类必须给所有类提供这一实例。
单例池:是用来是实现单例Bean的,单例池就是一个Map Bean对象的名字就是它的键,bean对象就是值。
FactoryBean的作用和底层
public class UserService { @Autowired UserMapper userMapper; public void test(){ System.out.println(userMapper.get()); } }
对于这个userMapper它就是MyBatils框架创建的代理对象,是由MyBatils创建的代理对象赋值给这个userMapper。
Spring想要使用这个MyBatils所产生的对象,就要把他放入到Spring容器中成为一个Bean,那么他是怎么让这个对象成为这个Bean的呢?
Spring事务的实现:
1 申明式:@Transan
2 编程式:使用TransaMananger类
如何定义一个Bean;
1 申明式:@Bean @Service <bean>....
2 编程式:使用BeanDefinition
package com.test.pojo; public class User { } package com.test; import com.test.pojo.User; import com.test.service.UserService; import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.ComponentScan; @ComponentScan("com.test") public class LuoGeApplaction implements InstantiationAwareBeanPostProcessor { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); context.register(LuoGeApplaction.class);//配置Spring //表示用来定义bean AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition(); beanDefinition.setBeanClass(User.class); context.refresh(); System.out.println(context.getBean("user")); // UserService userService = context.getBean("userService", UserService.class); // userService.test(); } } Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'user' available
我们这样式得不到这个User对象的,因为我们只是扫描了这个类,但是并没有将他放到我们Spring容器中,所以我们还需要一步。
@ComponentScan("com.test") public class LuoGeApplaction implements InstantiationAwareBeanPostProcessor { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); context.register(LuoGeApplaction.class);//配置Spring //表示用来定义bean AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition(); beanDefinition.setBeanClass(User.class); //将user对象注入到这个beanDefintion中。 context.registerBeanDefinition("user",beanDefinition); context.refresh(); System.out.println(context.getBean("user"));}} com.test.pojo.User@6dde5c8c
这就是我们的编程式得到Bean对象,那么我们的这些@Bean @Component....注解的底层肯定跟这个beanDefintion有很大的关系。也就是说式BeanDefinition实现的注解。
那么我们将User.class 换成UserService.class就可以得UserService对象嘛?
public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); context.register(LuoGeApplaction.class);//配置Spring //表示用来定义bean AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition(); beanDefinition.setBeanClass(UserMapper.class); //将user对象注入到这个beanDefintion中。 context.registerBeanDefinition("user",beanDefinition); context.refresh(); System.out.println(context.getBean("user")); org.springframework.context.support.AbstractApplicationContext refresh
答案式否定的,并不能得到UserService对象,下面我们就引出FactoryBean。
FactoryBean中有三个方法,你想去实现一个接口你就得实现他的方法,但是这里有默认实现,你就可以不用实现,也可以实现接口。(java8才可以,java7无法实现)
public class LuoGeBeanFactory implements FactoryBean { public Object getObject() throws Exception { return new User(); } public Class<?> getObjectType() { return User.class; } } public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); context.register(LuoGeApplaction.class);//配置Spring //表示用来定义bean AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition(); //此时的LuoGeBeanFactory对应了两个对象 beanDefinition.setBeanClass(LuoGeBeanFactory.class); //将user对象注入到这个beanDefintion中。 context.registerBeanDefinition("user",beanDefinition); context.refresh(); System.out.println(context.getBean("user")); System.out.println(context.getBean("&user")); com.test.pojo.User@35a50a4c com.mybatils.spring.LuoGeBeanFactory@1f021e6c
我们可以看到确实得到了LuoGeBeanFactory和User两个对象。那么现在我们就知道怎么才能得到这个MyBatils对象了,这也就是怎么注入MyBatils对象到Spring中。
@Component public class UserService { @Autowired UserMapper userMapper; public void test(){ System.out.println(userMapper.get()); } } public class LuoGeBeanFactory implements FactoryBean { public Object getObject() throws Exception { // 想要使用UserMapper对象, 我们就需要进行代理,这样我们就得到了这个代理对象。 Object o = Proxy.newProxyInstance(UserMapper.class.getClassLoader(), new Class[]{UserMapper.class}, new InvocationHandler() { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println(method.getName()); return null; } }); return o; } public Class<?> getObjectType() { return UserMapper.class; } } @ComponentScan("com.test") public class LuoGeApplaction implements InstantiationAwareBeanPostProcessor { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); context.register(LuoGeApplaction.class);//配置Spring //表示用来定义bean AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition(); beanDefinition.setBeanClass(LuoGeBeanFactory.class); //将user对象注入到这个beanDefintion中。 context.registerBeanDefinition("xxx",beanDefinition); context.refresh(); UserService userService = context.getBean("userService", UserService.class); userService.test(); } } get null
这里的get就是我们调用的那个方法,这个null是这个方法返回的null;
这里的null其实是userService对象,但是我们输出这个对象需要调用对象的toString方法。
<dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>2.0.6</version> </dependency>
这个jar包里面其实也就是在做我们刚才FactoryBean的事,但是我们发现了这个方法里面的UserService.class是定死了的Proxy.newProxyInstance(UserMapper.class.getClassLoader(), new Class[]{UserMapper.class}, new InvocationHandler()
问题又出现了,难道我们只能用这一个类吗?
public interface PeopleMapper { User set(); } public class LuoImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { List<Class> list = new ArrayList<Class>(); list.add(PeopleMapper.class); list.add(UserMapper.class); for (Class aClass : list) { //表示用来定义bean AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition(); beanDefinition.setBeanClass(LuoGeBeanFactory.class); beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(aClass); //将user对象注入到这个beanDefintion中。 registry.registerBeanDefinition(aClass.getName(),beanDefinition); } } } } @ComponentScan("com.test") @Import(LuoImportBeanDefinitionRegistrar.class) public class LuoGeApplaction implements InstantiationAwareBeanPostProcessor { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); context.register(LuoGeApplaction.class);//配置Spring context.refresh(); UserService userService = context.getBean("userService", UserService.class); userService.test(); } } null;
我们有了第二个Mapper的时候,我们就不能再把其中的类写死了,我们将它写成一个变量,再调用有参构造来注入它,当然我们再把除了启动Spring的代码移出去,移到ImportBeanDefinitionRegistrar类上,这也是我们启动前执行的类,将它通过@Imput注解将它注入到主要执行的代码中,他就可以实现不同的Mapper对象注入了。当然我们把所有需要的Mapper类都放到List集合中进行遍历就可以实现伪自动注入了。现在我么又引出一个问题,怎么才能扫描储所有的Mapper对象?
就是通过beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(aClass)方法把对应的类名传给LuoGeBeanFactory的有参构造进行赋值,但是因为我们在LuoGeBeanFactory存的就是mapper,再把beanDefinition的BeanClassName设置为原来的就可以了。
package com.mybatils.spring; import com.test.mapper.PeopleMapper; import com.test.mapper.UserMapper; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; import org.springframework.core.type.AnnotationMetadata; import org.springframework.core.type.classreading.MetadataReader; import org.springframework.core.type.classreading.MetadataReaderFactory; import org.springframework.core.type.filter.TypeFilter; import java.io.IOException; import java.util.ArrayList; import java.util.List; public class LuoImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { // 1 扫描 2 扫描路径 String path = "com.test.mapper"; //扫描注册表 LuoMapperScanner luoMapperScanner = new LuoMapperScanner(registry); luoMapperScanner.addIncludeFilter(new TypeFilter() { //由于这个scan扫描默认会排除一些东西,return true相当于不排除的意思,支持所有 public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException { return true; } }); int sacn = luoMapperScanner.scan(path);// 2个mapper System.out.println(sacn); } } package com.mybatils.spring; import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanDefinitionHolder; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.context.annotation.ClassPathBeanDefinitionScanner; import java.util.Set; public class LuoMapperScanner extends ClassPathBeanDefinitionScanner { //Spring 的扫描器 public LuoMapperScanner(BeanDefinitionRegistry registry) { super(registry); } @Override protected Set<BeanDefinitionHolder> doScan(String... basePackages) { // 这里得到beanDefinitionHolders就是扫描了Mapper Set<BeanDefinitionHolder> beanDefinitionHolders = super.doScan(basePackages); //我们得到的还是Mapper的接口,并不能得到对象 我们已经得到BeanFinition 并把他注入进去了 for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) { BeanDefinition beanDefinition = beanDefinitionHolder.getBeanDefinition(); //设置LuoGeBeanFactory类构造方法的参数值,将类名传入进行代理,得到代理对象 beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(beanDefinition.getBeanClassName()); //遍历出来的BeanClass是不对的 是个接口 我们修改它成为它所对应的class名因为我们一开始就是需要beanClass名称才能得到对应的接口类 beanDefinition.setBeanClassName(LuoGeBeanFactory.class.getName()); } return beanDefinitionHolders; } @Override protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) { //我们只需要找到属于接口的类跟Spring恰恰相反,Spring需要的是可以实现的类。 return beanDefinition.getMetadata().isInterface(); } } package com.mybatils.spring; import com.sun.org.apache.regexp.internal.RE; import com.test.mapper.UserMapper; import com.test.pojo.User; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class LuoGeBeanFactory implements FactoryBean { private Class mapperClass; private SqlSession sqlSession; public LuoGeBeanFactory(Class mapperClass) { this.mapperClass = mapperClass; } @Autowired public void setSqlSession(SqlSessionFactory sqlSessionFactory) { this.sqlSession = sqlSessionFactory.openSession(); } public Object getObject() throws Exception { // //想要使用UserMapper对象, 我们就需要进行代理,这样我们就得到了这个代理对象。 // Object o = Proxy.newProxyInstance(UserMapper.class.getClassLoader(), new Class[]{mapperClass}, new InvocationHandler() { // public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // System.out.println(method.getName()); // return null; // } // }); // return o; //直接通过sqlSession得到mapper return sqlSession.getMapper(mapperClass); } public Class<?> getObjectType() { return mapperClass; } } package com.test; import com.mybatils.spring.LuoGeBeanFactory; import com.mybatils.spring.LuoImportBeanDefinitionRegistrar; import com.test.mapper.UserMapper; import com.test.pojo.User; import com.test.service.UserService; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Import; import java.io.IOException; import java.io.InputStream; @ComponentScan("com.test") @Import(LuoImportBeanDefinitionRegistrar.class) public class LuoGeApplaction implements InstantiationAwareBeanPostProcessor { @Bean public SqlSessionFactory sqlSessionFactory() throws IOException { //只需要的到一个sqlSessionFactory 文件可以随便写 InputStream inputStream = Resources.getResourceAsStream("mybatis.xml"); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); return sqlSessionFactory; } public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); context.register(LuoGeApplaction.class);//配置Spring context.refrJ(); UserService userService = context.getBean("userService", UserService.class); userService.test(); } } 2 people people
通过LuoMapperScanner调用scan底层就是doScan方法进行对这个包的扫描的到我们想要的Mapper对象,再dosacn方法中我们我们取出本来就存入了beanDefinition的BeanClass名字(),再将它设置为原来的名字设置为beanDefinitionHolders(这里得到beanDefinitionHolders就是扫描了Mapper),这里我们就得到了mapper层的所有接口类了,所以scan的值是2,这里我们再通过方法的到SqlSessionFactory对象,SqlSessionFactory就可以直接getMapper了,这就是我们的Spring Mybatis的整合基本原理。