spring

spring的初始化

1、spring创建对象时通过反射来创建对象的,底层第通过无参构造器,如果没有无参构造器会报以下错误:
	-- NoSuchMethodException: cn.wolfcode._01_hello.Person.<init>()
2、spring在获取ClassPathXmlApplicationContext("_01_hello.xml")对象时就扫描xml中的所有的<bean>标签,并为其创建了对象存放在spring容器中
3、获取指定<bean>对象的步骤
      - 获取ApplicationContext act = ClassPathXmlApplicationContext("Xxx.xml");
	  - 获取对应的bean对象  act.getBean("id");
	  - 可以通过字节码Xxx.class获取(当bean只有一个时): act.getBean(Xxx.class)

springtest测试

@RunWith(SpringJUnit4ClassRunner.class)
//@RunWith注解是JUnit框架的一部分,它用于指定测试类或测试方法运行时使用的特殊测试运行器。它通常与@org.junit.Test注解一起使用。

//例如,在使用Spring框架时,我们可以使用@RunWith(SpringJUnit4ClassRunner.class)注解来使用Spring提供的JUnit运行器来运行我们的测试类
@ContextConfiguration("classpath:_02_ioc.xml")//要测试的bean的xml文件
public class MySpringTest {

    @Autowired
    private Cat cat;

    @Test
    public void test(){
        cat.say();
    }
}
---  如果需要同时测试多个配置文件可以在@ContextConfiguration里面配置多个
    @ContextConfiguration({"classpath:A.xml","classpath:B.xml"})//要测试的bean的xml文件
    

使用ApplicationContext获取bean的三种方式

getBean("id或name")
getBean(类名.class)	//推荐使用,因为一般bean中只有一个类型的类
getBean("id或name",类名.class)

使用@Autowired时,它会先使用byType自动注入进行找对应类型的bean,如果有两个相同类型的bean时,那么它会按byName去找对应的bean,如果没有会报错;在对于有多个类型的bean时,可以使用@Qualifier(“id”)来指定自动注入哪个bean

	<bean id="dog1" class="cn.wolfcode2._06_outoWire.Dog"></bean>
    <bean id="dog2" class="cn.wolfcode2._06_outoWire.Dog"></bean>

	@Autowired
    @Qualifier("dog1")
    private Dog dog;
//因此使用了 @Qualifier 注解来指定要注入的 bean 的 ID 为 "dog1"。这样就可以明确指定要注入的 bean,避免了出现 NoUniqueBeanDefinitionException 异常的情况。
一、spring配置别名和设置非单例对象
- 别名 : userDAO 和 dao 都可以作为id使用
- 作用范围: prototype(非单例,默认),singleton(单例)
<bean id="userDAO" name="dao" class="全限定名" scope="prototype"/>
    
--  适合交给容器管理的bean
    -表现层对象	-业务层对象	-数据层对象	-工具对象
--  不适合交给容器管理的bean
    -domain下的封装实体类对象

spring是使用无参的构造方建对象的,底层使用的是反射

-证明使用无参构造方法
    创建一个带参的构造器,会发现创建不了对象
-证明底层使用反射
    可以私有化构造器,发现还是能创建出对象
二、实例化bean的三种方式
  1. 构造方法(常用
    -配置<bean>
        <bean id="userDAO" class="UserDAOImpl类的全限定名" />
    
  2. 静态工厂
    -需要三个成员(接口、实现类、工厂)
    -工厂类的写法
        public class OrderDAOFactory{
            public static OrderDAO getOrderDAO(){
                return new OrderDAOImpl();
            }
        }
    
  3. FactoryBean(实用
public class UserDaoFactoryBean implements FactoryBean<UserDAO>{
    //userDAO是指定生产对象的类型,指定的泛型T
    //实现getObject()方法,返回一个实例对象	return new UserDAOImpl();
    //实现getObjectType()方法,返回一个字节码对象		return UserDAO.class;
    //方法isSingleton()不写就是单例  返回true就是单例,返回false就是非单例
}
- 配置<bean>
    <bean id="userDAO" class="UserDaoFactoryBean类的全限定名" />
    它会根据工厂的getObject()方法创建出对应的对象,类型是getObjectType()返回的类型
---  在bean中使用接口时需要使用这种方式,因为接口不能直接创建对象
三、依赖注入
注入方式:
-setter注入
    -简单类型	-引用类型
比如配置引用类型:
public class BookServiceImpl implements BookService{
    private BookDAO bookDAO;
    //写一个BookDAO的 setter方法
}
-配置<bean>
    <bean id="bookDAO" class="BookDAOImpl实现类全限定名"/>	
    <bean id="bookService" class="BookServiceImpl实现类全限定名">
        //name是字段名,ref是<bean>的id
        <property name="bookDAO" ref="bookDAO"/>
    </bean>
        
配置普通类型(value直接给值就行了)   
   <property name="实现类中的字段名" value="10"/>
=================================================================
-构造方法注入(就是调用有参构造器创建对象了)
    -简单类型	-引用类型
引用类型写法
    <bean id="bookDAO" class="BookDAOImpl实现类全限定名"/>	
    <bean id="bookService" class="BookServiceImpl实现类全限定名">
        //name是构造器的形参名,ref是<bean>的id
        <constructor-arg name="bookDAO" ref="bookDAO"/>	//多个参数可以使用多行
    </bean> 
配置普通类型(value直接给值就行了)   
   <constructor-arg name="构造器的形参名" value="10"/>  
四、自动注入(自动装配)
  • byType按类型注入(对应的类的bean只能有一个,而且必须要setter方法),中可以去掉name
//service实现类有BookDAO字段
public class BookServiceImpl implements BookService{	
    private BookDAO bookDAO;
    //写一个BookDAO的 setter方法
    public void save(){
        System.out.println("service ...");
        bookDAO.save();	//使用到BookDAO类型对象,会自动去<bean>配置中找该类型的bean
    }
}

-配置<bean>
    <bean id="bookDAO" class="BookDAOImpl实现类全限定名"/>	
    <bean id="bookService" class="BookServiceImpl实现类全限定名" autowire="byType">
  • byName注入(哪个的id名和setter方法名对应的属性一样,就注入哪个,没有就报空指针)
依赖自动注入的特征
  • 自动注入用于引用类型依赖注入,不能对简单类型进行操作
  • 使用按类型注入时(byType)必须保障容器中相同类型的bean唯一,推荐使用
  • 使用按名称注入时(byName)必须保障容器中具有指定名称的bean,因属性名与配置耦合,不推荐使用
  • 自动注入优先级低于setter注入与构造器注入,同时出现时自动注入配置失效
注解之@Autowired和@Value(掌握)

1、@Autowired注解是先进行getBean(class clz)byType进行注入,如果有多个相同的类型的时候,会通过类型和名称一起匹配查找getBean(“id或name”,class clz),如果没有则报错

@Value可以直接给普通对象属性进行赋值

Ioc注解(掌握)
  • @Repository : 用于标注在DAO实现类上
  • @Service : 用于标注在业务层实现类上
  • @Controller : 用于标注在控制层类上(如springMVC的Controller)
  • @Component : 除了以上这些的话,其他可以使用这个注解进行标注
Scope和PostConstruct以及PreDestroy注解
  • @Scope : 帖在类上,标明bean的作用域
  • @PostContruct : 贴在方法上,比小明创建完后调用此方法。
  • @PreDestroy : 贴在方法上,标明销毁时调用此方法

注意:当作用域scope的属性值为prototype(原型,非单例)时,它的销毁操作方法是不会被调用的,需要手动来调用;当一个类不是单例的时候,spring容器是不会管理该类完整的生命周期的

五、集合注入

数组、List、Set、Map、properties

--- 数组、ListSet都是一样的,只不过set会自动去重
<bean id="bookDAO" class="BookDAO实现类全限定名">
    <property name="array">
    	<array>
    		<value>10</value>
    		<value>20</value>
    	</array>
     </property>
    <property name="map">
    	<map>
    		<entry key="country" value="china"/>
    		<entry key="city" value="zhangjiang"/>
    	</map>
     </property>
    <property name="properties">
    	<props>
    		<prop key="country">china</prop>
    		<prop key="city">zhangjiang</prop>
    	</props>
     </property>
</bean>
    

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-82IxQq2w-1685604744318)(C:\Users\24128\AppData\Roaming\Typora\typora-user-images\image-20230412231650903.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8oOn0esx-1685604744319)(C:\Users\24128\AppData\Roaming\Typora\typora-user-images\image-20230412232102682.png)]

六、动态代理

步骤:

InvocationHandler接口的底层步骤和逻辑
InvocationHandler 是一个接口,它包含了一个 invoke() 方法,用于在代理对象上调用方法时进行处理。其底层步骤和逻辑如下:

1、创建一个实现了 InvocationHandler 接口的代理对象,并将目标对象传入代理构造函数(给target真实对象赋值)。
2、当代理对象的方法被调用时,将被重定向到 invoke() 方法。
3invoke() 方法接收到方法名、参数和代理对象等信息。
4、根据方法名和参数等信息,可以自定义处理逻辑,例如在调用目标方法前后添加一些额外的逻辑。
5invoke() 方法将处理结果返回给代理对象。
6、代理对象将结果返回给调用者。
	总体来说,InvocationHandler 接口允许在方法调用前后添加自定义逻辑,从而扩展目标对象的功能。它提供了一种比继承更灵活的方式,因为它可以在运行时动态地添加和删除方法,并在需要时修改代理的行为。通常情况下,我们可以使用 InvocationHandler 接口实现代理模式、AOP 等功能。
jdk动态代理

1、创建被代理类对象

public class PersonDAOImpl implements IPersonDAO {

    public void sayHello(String name) {
        System.out.println("你好:"+name);
    }

    public void add() {
        System.out.println("执行添加操作");
    }
}

2、创建一个动态代理类来实现java.lang.reflect.InvocationHandler;接口并实现其中的**invoke()**方法

​ — 方法中使用该实现类的构造器来给被代理类进行初始化赋值

public class PersonInvocationHandler implements InvocationHandler {
    private Object obj;	//被代理类对象

    public PersonInvocationHandler(Object obj) {
        //利用代理类的构造器给被代理类对象进行初始化
        this.obj = obj;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("开启事物");

        //被代理类实行的方法
        Object result = method.invoke(obj, args);

        System.out.println("关闭事物,资源");
        return result;
    }
}

3、测试类实行的代码

@Test
    public void test2(){
        IPersonDAO person = new PersonDAOImpl();//创建一个被代理类对象

        IPersonDAO result = (IPersonDAO) Proxy.newProxyInstance(
                            person.getClass().getClassLoader(),
                            person.getClass().getInterfaces(),
                            new PersonInvocationHandler(person));
        result.add();
    }

使用newProxyInstance()方法时,就会创建一个被代理对象,当被代理对象执行对应的方法时,就会重定向去调用InvacationHandler.invoke,invoke方法中执行时就是执行被代理类对象(真实类型对象)执行的方法,因为在创建代理对象的时候,其中的第三个参数就已经存储了被代理类的真实类的类型了,所以里面执行的就是真实类的方法

使用cglib动态代理

1、创建一个真实类

2、创建一个代理类实现org.springframework.cglib.proxy.InvocationHandler接口

3、书写xml文件

<bean id="tx" class="cn.wolfcode2.cglib.MyTransactionManager"></bean>

    <bean id="translationHandler" class="cn.wolfcode2.cglib.proxy.MyTranslationHandler">
        <property name="tx" ref="tx"/>
        <property name="target">
            <bean id="userService" class="cn.wolfcode2.cglib.service.impl.UserServiceImpl"></bean>
   //里层的bean只对外层的bean可用
        </property>
    </bean>

4、书写测试类测试代码

Enhancer是CGLIB库中的一个类,用于生成代理类。它通过创建目标类的子类来实现代理,从而避免了代理类必须实现接口的限制,因此它也被称为“子类代理”。Enhancer通过设置一系列的回调函数(Callback)来拦截对目标类方法的调用,并在拦截时执行自己的逻辑。

enhancer.setSuperclass() : 用于设置代理类的父类,即被代理类。CGLIB可以生成一个继承于被代理类的代理类,从而实现代理功能。在设置代理类父类时,需要使用setSuperclass方法,传入被代理类的Class对象。

setcallback() : 当使用 CGLIB 实现动态代理时,需要设置回调方法才能在代理对象方法被调用时执行相应的逻辑,否则会出现空指针异常原因是代理对象在被调用时会去执行回调方法,如果回调方法没有被设置,则会为空,从而导致空指针异常。

create() : 方法返回的对象是一个代理类的实例对象,它继承了被代理类的所有非私有方法,并且可以覆盖这些方法。

		Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(handler.getTarget().getClass());

        enhancer.setCallback(handler);

        UserServiceImpl userService = (UserServiceImpl) enhancer.create();//创建一个真实类的子类对象,包含真实类的字段和方法
        userService.check();
使用javassist动态代理
public class JavassistProxyFactory {	//代理工厂类
    public static Object createProxy(Class targetClass, MethodHandler handler) throws Exception {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(targetClass);	
        //传入被代理的字节码,CGLIB可以生成一个继承于被代理类的代理类
        enhancer.setCallback(handler);
        Class<?> proxyClass = enhancer.createClass();
        //创建代理类的字节码,在调用该方法前必须设置好回调方法
        Object proxyInstance = proxyClass.newInstance();
        return proxyInstance;
    }
}

public class JavassistHandler implements MethodHandler {	//代理处理器
    private Object target;

    public JavassistHandler(Object target) {
        this.target = target;
    }

    public Object invoke(Object proxy, Method method, Method proceed, Object[] args) throws Throwable {
        System.out.println("执行前操作===");
        Object result = method.invoke(target, args);
        System.out.println("执行方法后的操作===");
        return result;
    }
}

public class UserServiceImpl {
    public void save() {
        System.out.println("save user");
    }
}

public class JavassistProxyTest {
    @Test
    public void testJavassistProxy() throws Exception {
        UserServiceImpl userService = new UserServiceImpl();
        JavassistHandler handler = new JavassistHandler(userService);
        UserServiceImpl proxy = (UserServiceImpl) JavassistProxyFactory.createProxy(UserServiceImpl.class, handler);
        proxy.save();
    }
}

七、使用JavaConfig来获取bean,无需配置xml

1、先创建实体类

@Component
public class User {
    @Value("战三")
    private String name;
    @Value("123456")
    private String password;
}
@Component
public class Dog {
    @Value("二哈")
    private String name;
}

2、创建一个生产实体类的类(javaConfig类)

@Configulation : 用于标注一个类为配置类,相当于.xml

​ 作用:可以让spring容器在启动时自动扫描配置类,并将其中声明的bean加入到容器中

@Bean : 用于声明bean对象,相当于bean标签

@ComponentScan():属性默认扫描的是当前包及其子包,使用了这个后配置类中可以不用使用@Bean,spring会默认把对应的包下的bean加入容器中,其他的类要加@Component注解

@Inport:用于导入一个或多个配置类或组件类(多个可以用{}括起来),将其导入到当前类中,以实现一些额外的功能

@Configuration	//指定它是生产bean的类
@ComponentScan("cn.wolfcode._01_springConfig")	//扫描器
@Import(ConfigDemo1.class)		//导入其他的生产bean的类,管理起来可以统一通过这个主类去获取
public class ConfigDemo {

    @Bean					//相当于xml中的bean标签,表明这个bean要交给spring容器管理
    public User getUser() {
        return new User();
    }
}

---   被关联的生产bean的类
    @Configuration
public class ConfigDemo1 {

    @Bean
    public Dog getDog() {
        return new Dog();
    }
}

3、使用测试类去获取指定的bean

@Test
    public void test1(){
        //不使用xml配置文件来获取bean
        ApplicationContext context = new AnnotationConfigApplicationContext(ConfigDemo.class);
        //ConfigDemo.class关联了生产Dog类的类
        //User user = (User) context.getBean("user");	//通过名字来获取
        User user = (User) context.getBean("getUser");	//通过方法来获取
        Dog dog = (Dog) context.getBean("getDog");		
        System.out.println(user);
        System.out.println(dog);
    }

4、使用spring测试类来测试配置类

@Configuration
public class AppConfig {

    @Bean
    public DataSource dataSource() {
        // 配置并返回数据源
        return new DruidDataSource();
    }
}

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {AppConfig.class})//可以对多个配置类进行测试
public class MyTest {

    // 测试方法
}

八、spring的ioc原理

1、spring中ioc的原理是使用xml解析、反射和工厂模式来实现的

xml解析: 通过解析来获得其中的class的值

<bean class="cn.wolfcode.mapper.UserMapper"></bean>

**反射:**通过xml解析获得的class的值,再进行Class.forName(“xxx.xxx.xxx”)来获得对应的字节码对象clz,再通过字节码对象的clz.newInstance()来创建对象

Class clz = Class.forName("xxx.xxx.xxx.xx");
UserMapper userMapper = clz.newInstance();

**工厂模式:**通过工厂模式来返回一个该对象,定义一个静态方法用过返回反射创建好的对象

public class UserMapperFactory(){
    public static Object getBean(){
        return clz.newInstance();
    }
}

2、spring中有两个常用的接口(BeanFactory、ApplicationContext),ApplicationContext是开发人员常用的接口

**BeanFactory接口 😗*在获取spring容器的时候并没有创建出bean对象,而是在使用bean的时候才开始创建bean对象的

**ApplicationContext接口:**在获取spring容器的时候就创建好了bean对象了

**常用的两个实现类:**ClassPathXmlApplicationContext、FileSystemXmlApplicationContext

ClassPathXmlApplicationContext:从字节码的根路径下开始查找xml文件
FileSystemXmlApplicationContext:从电脑磁盘的的路径下找对应的xml文件

public class UserMapperFactory(){
    public static Object getBean(){
        return clz.newInstance();
    }
}

2、spring中有两个常用的接口(BeanFactory、ApplicationContext),ApplicationContext是开发人员常用的接口

**BeanFactory接口 😗*在获取spring容器的时候并没有创建出bean对象,而是在使用bean的时候才开始创建bean对象的

**ApplicationContext接口:**在获取spring容器的时候就创建好了bean对象了

**常用的两个实现类:**ClassPathXmlApplicationContext、FileSystemXmlApplicationContext

ClassPathXmlApplicationContext:从字节码的根路径下开始查找xml文件
FileSystemXmlApplicationContext:从电脑磁盘的的路径下找对应的xml文件
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值