Spring 注解开发详解

1. 注解驱动入门案例介绍

1.1 需求描述

1.需求:实现保存一条数据到数据库。
2.表结构:create table account(id int primary key auto_increment,name varchar(50),money double(7,2));
3.要求:使用spring框架中的JdbcTemplate和DriverManagerDataSource使用纯注解配置spring的ioc

1.2 案例实现

  • 导入坐标
<dependencies><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.1.6.RELEASE</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>5.1.6.RELEASE</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.45</version></dependency>
</dependencies>
  • 编写配置类
/*** spring的配置类* 用于替代xml配置*/
@Configuration
@Import(JdbcConfig.class)
@PropertySource("classpath:jdbc.properties")
public class SpringConfiguration {
}/*** 连接数据库的配置* @author 黑马程序员* @Company http://www.itheima.com*/
public class JdbcConfig {@Value("${jdbc.driver}")private String driver;@Value("${jdbc.url}")private String url;@Value("${jdbc.username}")private String username;@Value("${jdbc.password}")private String password;@Bean("jdbcTemplate")public JdbcTemplate createJdbcTemplate(DataSource dataSource){return new JdbcTemplate(dataSource);}@Bean("dataSource")public DataSource createDataSource(){DriverManagerDataSource dataSource = new DriverManagerDataSource();dataSource.setDriverClassName(driver);dataSource.setUrl(url);dataSource.setUsername(username);dataSource.setPassword(password);return dataSource;}
}
  • 编写配置文件
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spring_day01
jdbc.username=root
jdbc.password=1234
  • 编写测试类
public class SpringAnnotationDrivenTest {/*** 测试* @param args*/public static void main(String[] args) {//1.获取容器ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);//2.根据id获取对象JdbcTemplate jdbcTemplate = ac.getBean("jdbcTemplate",JdbcTemplate.class);//3.执行操作jdbcTemplate.update("insert into account(name,money)values(?,?)","test",12345);}
}

2. IOC的常用注解分析

2.1 @Configuration

2.1.1 源码

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {/*** Explicitly specify the name of the Spring bean definition associated with the* {@code @Configuration} class. If left unspecified (the common case), a bean* name will be automatically generated.* <p>The custom name applies only if the {@code @Configuration} class is picked* up via component scanning or supplied directly to an* {@link AnnotationConfigApplicationContext}. If the {@code @Configuration} class* is registered as a traditional XML bean definition, the name/id of the bean* element will take precedence.* @return the explicit component name, if any (or empty String otherwise)* @see org.springframework.beans.factory.support.DefaultBeanNameGenerator*/@AliasFor(annotation = Component.class)String value() default "";
}

2.1.2 说明

它是在spring3.0版本之后加入的。此注解是spring支持注解驱动开发的一个标志。表明当前类是spring的一个配置类,作用是替代spring的applicationContext.xml。但其本质就是@Component注解,被此注解修饰的类,也会被存入spring的ioc容器。

使用场景:
在注解驱动开发时,用于编写配置的类,通常可以使用此注解。一般情况下,我们的配置也会分为主从配置,@Configuration一般出现在主配置类上。值得注意的是,如果我们在注解驱动开发时,构建ioc容器使用的是传入字节码的构造函数,此注解可以省略。但是如果传入的是一个包,此注解则不能省略。

属性:
value: 用于存入spring的Ioc容器中Bean的id。

2.1.3 示例

在注解驱动的入门案例中,由于没有了applicationContext.xml,就没法在xml中配置spring创建容器要扫描的包了。那么,我们自己写的一些类,通过注解配置到ioc容器中也无法实现了。此时就可以使用此注解来代替spring的配置文件。

/*** spring的配置类* 用于替代xml配置*/
@Configuration
@Import(JdbcConfig.class)
@PropertySource("classpath:jdbc.properties")
@ComponentScan("com.test")
public class SpringConfiguration {
}

2.2 @ComponentScan

2.2.1、源码

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {/***/@AliasFor("basePackages")String[] value() default {};/***/@AliasFor("value")String[] basePackages() default {};/***/Class<?>[] basePackageClasses() default {};/***/Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;/***/Class<? extends ScopeMetadataResolver> scopeResolver() default AnnotationScopeMetadataResolver.class;/***/ScopedProxyMode scopedProxy() default ScopedProxyMode.DEFAULT;/***/String resourcePattern() default ClassPathScanningCandidateComponentProvider.DEFAULT_RESOURCE_PATTERN;/***/boolean useDefaultFilters() default true;/***/Filter[] includeFilters() default {};/***/Filter[] excludeFilters() default {};/***/boolean lazyInit() default false;/*** Declares the type filter to be used as an {@linkplain ComponentScan#includeFilters* include filter} or {@linkplain ComponentScan#excludeFilters exclude filter}.*/@Retention(RetentionPolicy.RUNTIME)@Target({})@interface Filter {/***/FilterType type() default FilterType.ANNOTATION;/***/@AliasFor("classes")Class<?>[] value() default {};/***/@AliasFor("value")Class<?>[] classes() default {};/***/String[] pattern() default {};}
}

2.2.2 说明

  • 作用:
    用于指定创建容器时要扫描的包。该注解在指定扫描位置时,可以指定包名,也可以指定扫描类。同时支持定义扫描规则,例如包含哪些或者排除哪些。同时,它还支持自定义Bean的命名规则。

  • 属性:

    value: 用于指定要扫描的包。当指定包名称之后,spring会扫描指定包及其子包下的所有类。
    basePackages: 它和value作用是一样的。
    basePackageClasses: 指定具体要扫描的类的字节码。
    nameGenrator: 指定扫描bean对象存入容器时的命名规则。
    scopeResolver: 用于处理并转换检测到的Bean的作用范围。
    soperdProxy: 用于指定bean生成时的代理方式。默认是Default,则不使用代理。
    resourcePattern: 用于指定符合组件检测条件的类文件,默认是包扫描下的 **/*.class
    useDefaultFilters: 是否对带有@Component, @Repository, @Service, @Controller注解的类开启检测,默认是开启的。
    includeFilters: 自定义组件扫描的过滤规则,用以扫描组件。
    excludeFilters: 自定义组件扫描的排除规则。
    lazyInit: 组件扫描时是否采用懒加载 ,默认不开启。

  • 使用场景:

    在注解驱动开发时,我们自己编写的类都使用注解的方式进行配置,要想让spring添加到ioc容器中,就需要使用此注解来实现组件的扫描。

2.2.3 示例

在入门案例中,如果我们加入了dao或者记录日志的工具类,这些使用了@Component或其衍生注解配置的bean,要想让他们进入ioc容器,就需要使用@ComponentScan。

package com.test.dao.impl;
import com.test.dao.AccountDao;      @Repository("accountDao")
public class AccountDaoImpl implements AccountDao{//持久层开发
}      /*** spring的配置类* 用于替代xml配置*/
@Configuration
@Import(JdbcConfig.class)
@PropertySource("classpath:jdbc.properties")
@ComponentScan("com.test")
public class SpringConfiguration {
}

2.3 @Bean

2.3.1 源码

package org.springframework.context.annotation;import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.core.annotation.AliasFor;@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Bean {/***/@AliasFor("name")String[] value() default {};/***/@AliasFor("value")String[] name() default {};/***/@DeprecatedAutowire autowire() default Autowire.NO;/***/boolean autowireCandidate() default true;/***/String initMethod() default "";/***/String destroyMethod() default AbstractBeanDefinition.INFER_METHOD;
}	

2.3.2 说明

  • 作用:

    它写在方法上,表示把当前方法的返回值存入spring的ioc容器。同时还可以出现在注解上,作为元注解来使用。

  • 属性:
    name: 用于指定存入spring容器中bean的标识。支持指定多个标识。当不指定该属性时,默认值是当前方法的名称。
    value: 它和name属性的作用是一样的。
    autowireCandidate: 用于指定是否支持自动按类型注入到其他bean中。只影响@Autowired注解的使用,不影响@Resource注解注入。默认值为true,意为允许使用自动按类型注入。
    initMethod: 用于指定初始化方法。
    destroyMethod: 用于指定销毁方法。

  • 使用场景:

    通常情况下,在基于注解的配置中,我们用于把一个类存入spring的ioc容器中,首先考虑的是使用@Component以及他的衍生注解。但是如果遇到要存入容器的Bean对象不是我们写的类,此时无法在类上添加@Component注解,这时就需要此注解了。

2.3.3 示例:

例如,在我们配置JdbcTemplate使用Spring内置数据源DriverManagerDataSource时,数据源类是spring-jdbc这个jar包中类,此时我们无法编辑,此时就可以使用@Bean注解配置。

@Bean("jdbcTemplate")
public JdbcTemplate createJdbcTemplate(DataSource dataSource){return new JdbcTemplate(dataSource);
}

2.4 @Import

2.4.1 源码

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {/***/Class<?>[] value();
}

2.4.2 说明

  • 作用:

    该注解是使用在类上的,通常和注解驱动的配置类一起使用,其作用是引入其他配置类。使用此注解之后,可以使我们的注解驱动开发和早期xml配置一样,分别配置不同的内容,使配置更加清晰。同时指定此注解之后,被引入的类上可以不再使用@Configuration,@Component等注解。

  • 属性:

    value:用于指定其他配置类的字节码。它支持指定多个配置类。

  • 使用场景:

    当我们在使用注解驱动开发时,由于配置项过多,如果都写在一个类里面,配置结构和内容将杂乱不堪,此时使用此注解可以把配置项分门别类进行配置。

2.4.3 示例

在入门案例中,我们使用了SpringConfiguration做为主配置类,而连接数据库相关的配置被分配到了JdbcConfig配置类中,此时在SpringConfiguration类上使用@Import注解就可以把JdbcConfig导入进来了。

 /*** spring的配置类* 用于替代xml配置*/
@Configuration
@Import(JdbcConfig.class)
@PropertySource("classpath:jdbc.properties")
@ComponentScan("com.test")
public class SpringConfiguration {
}/*** 连接数据库的配置*/
public class JdbcConfig {@Value("${jdbc.driver}")private String driver;@Value("${jdbc.url}")private String url;@Value("${jdbc.username}")private String username;@Value("${jdbc.password}")private String password;@Bean("jdbcTemplate")public JdbcTemplate createJdbcTemplate(DataSource dataSource){return new JdbcTemplate(dataSource);}@Bean("dataSource")public DataSource createDataSource(){DriverManagerDataSource dataSource = new DriverManagerDataSource();dataSource.setDriverClassName(driver);dataSource.setUrl(url);dataSource.setUsername(username);dataSource.setPassword(password);return dataSource;}
}

2.5 @PropertySource

2.5.1 源码

@Retention(RetentionPolicy.RUNTIME)
@Documented
@Repeatable(PropertySources.class)
public @interface PropertySource {/***/String name() default "";/** */String[] value();/** */boolean ignoreResourceNotFound() default false;/***/String encoding() default "";/***/Class<? extends PropertySourceFactory> factory() default PropertySourceFactory.class;}

2.5.2 说明

  • 作用:

    用于指定读取资源文件的位置。注意,它不仅支持properties,也支持xml文件,并且通过YAML解析器,配合自定义PropertySourceFactory实现解析yml配置文件。

  • 属性:

    name : 指定资源的名称。如果没有指定,将根据基础资源描述生成。
    value : 指定资源的位置。可以是类路径,也可以是文件路径。
    ignoreResourceNotFound : 指定是否忽略资源文件是否存在,默认false,即当资源文件不存在时spring启动将会报错。
    encoding : 指定解析资源文件使用的字符集。当有中文的时候,需要指定中文的字符集。
    factory : 指定读取对应资源文件的工厂类,默认的是PropertySourceFactory。

  • 使用场景:

    开发中使用注解驱动后,xml配置文件就没有了,此时一些配置如果直接写在类中,会造成和java源码的紧密耦合,修改起来不方便。此时可以使用properties或者yml来配置。

2.5.3 示例

在入门案例中,我们连接数据库的信息如果直接写到JdbcConfig类中,当需要修改时,就面临修改源码的问题,此时使用@PropertySource就可以把配置放到properties文件中了。

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spring_day01
jdbc.username=root
jdbc.password=1234
/*** 连接数据库的配置*/
public class JdbcConfig {@Value("${jdbc.driver}")private String driver;@Value("${jdbc.url}")private String url;@Value("${jdbc.username}")private String username;@Value("${jdbc.password}")private String password;
}/*** spring的配置类* 用于替代xml配置*/
@Configuration
@Import(JdbcConfig.class)
@PropertySource(value = "classpath:jdbc.properties")
@ComponentScan("com.test")
public class SpringConfiguration {
}

3. 注解驱动开发之注入时机和设定注入条件的注解

3.1 @DependsOn

3.1.1 源码

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DependsOn {String[] value() default {};
}

3.1.2 说明

  • 作用:

    用于指定Bean之间依赖关系的一个注解。spring中没有指定bean的加载顺序,使用此注解则可指定bean的加载顺序。(在基于注解配置中,是按照类中方法的书写顺序决定的)

  • 属性:

    value: 用于指定bean的唯一标识。被指定的bean会在当前bean创建之前加载。

  • 使用场景:

    在观察者模式中,分为事件,事件源和监听器。一般情况下,监听器负责监听事件源,当事件源触发事件之后,监听器就要捕获,并且做出相应的处理。所以我们肯定希望监听器的创建时间在事件源之前,此时就可以使用此注解。

3.1.3 示例

/*** @author hao*/
@Component
public class CustomerListener {public CustomerListener(){System.out.println("监听器创建了。。。");}
}/*** @author hao*/
@Component
@DependsOn("customerListener")
public class Customer {public Customer(){System.out.println("事件源创建了。。。");}
}

在这里插入图片描述

3.2 @Lazy

3.2.1 源码

@Target({ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Lazy {boolean value() default true;
}

3.2.2 说明

  • 作用:

    用于指定单例bean对象的创建时机。在没有使用此注解时,单例bean的生命周期与容器相同。但使用此注解之后,单例对象的创建时机变成了第一次使用时创建。注意:这不是延迟加载思想(因为不是每次使用时都创建,只是第一次创建的时机改变了)。

  • 属性:

    value : 指定是否采用延迟加载。默认值为true,表示开启。

  • 使用场景:

    在实际开发中,当我们的Bean是单例对象时,并不是每个都需要一开始都加载到ioc容器之中,有些对象可以在真正使用的时候再加载,当有此需求时,即可使用此注解。值得注意的是,此注解只对单例bean对象起作用,当指定了@Scope注解的prototype取值后,此注解不起作用。

3.2.3 示例

/*** @author hao*/
@Component
@Lazy
//@Lazy(value = false)
//@Scope("prototype")
public class LazyBean {public LazyBean(){System.out.println("LazyBean对象创建了");}
}/*** @author hao*/
public class SpringAnnotationDrivenTest {/*** 测试* @param args*/public static void main(String[] args) {//1.获取容器ApplicationContext ac = new AnnotationConfigApplicationContext("config");//2.根据id获取对象  LazyBean lazyBean = (LazyBean)ac.getBean("lazyBean");System.out.println(lazyBean);}}

3.3 @Conditional

3.3.1 源码

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {/*** All {@link Condition Conditions} that must {@linkplain Condition#matches match}* in order for the component to be registered.*/Class<? extends Condition>[] value();
}	

3.3.2 说明

  • 作用:

    它的作用是根据条件选择注入bean对象

  • 属性:

    value : 用于提供一个Condition接口的实现类,实现类中需要编写具体代码实现注入的条件。

  • 使用场景:

    当我们在开发时,可能会使用多平台来测试,例如我们的测试数据库分别部署到了linux和windows两个操作系统上面,现在根据我们的工程运行环境选择连接的数据库。此时就可以使用此注解。同时基于此注解引出的@Profile注解,就是根据不同的环境,加载不同的配置信息。

3.3.3 示例

/*** 连接数据库的配置* @author hao*/
public class JdbcConfig {@Value("${jdbc.driver}")private String driver;@Value("${jdbc.url}")private String url;@Value("${jdbc.username}")private String username;@Value("${jdbc.password}")private String password;/*** linux系统注入的数据源* @param lDriver* @param lUrl* @param lUsername* @param lPassword* @return*/@Bean("dataSource")@Conditional(LinuxCondition.class)public DataSource createLinuxDataSource(@Value("${linux.driver}") String lDriver,@Value("${linux.url}")String lUrl,@Value("${linux.username}")String lUsername,@Value("${linux.password}")String lPassword){DriverManagerDataSource dataSource = new DriverManagerDataSource(lUrl,lUsername,lPassword);dataSource.setDriverClassName(lDriver);System.out.println(lUrl);return dataSource;}/*** windows系统注入的数据源* @return*/@Bean("dataSource")@Conditional(WindowsCondition.class)public DataSource createWindowsDataSource(){DriverManagerDataSource dataSource = new DriverManagerDataSource();dataSource.setDriverClassName(driver);dataSource.setUrl(url);dataSource.setUsername(username);dataSource.setPassword(password);System.out.println(url);return dataSource;}
}/*** @author hao*/
public class LinuxCondition implements Condition {@Overridepublic boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {//获取ioc使用的beanFactoryConfigurableListableBeanFactory beanFactory = context.getBeanFactory();//获取类加载器ClassLoader classLoader = context.getClassLoader();//获取当前环境信息Environment environment = context.getEnvironment();//获取bean定义的注册类BeanDefinitionRegistry registry = context.getRegistry();//获得当前系统名String property = environment.getProperty("os.name");//包含Windows则说明是windows系统,返回trueif (property.contains("Linux")){return true;}return false;}
}/*** @author hao*/
public class WindowsCondition implements Condition {@Overridepublic boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {//获取ioc使用的beanFactoryConfigurableListableBeanFactory beanFactory = context.getBeanFactory();//获取类加载器ClassLoader classLoader = context.getClassLoader();//获取当前环境信息Environment environment = context.getEnvironment();/*** 获取所有系统环境变量*/if(environment instanceof StandardEnvironment){StandardEnvironment standardEnvironment = (StandardEnvironment)environment;Map<String,Object> map = standardEnvironment.getSystemProperties();for(Map.Entry<String,Object> me : map.entrySet()){System.out.println(me.getKey()+","+me.getValue());}}//获取bean定义的注册类BeanDefinitionRegistry registry = context.getRegistry();//获得当前系统名String property = environment.getProperty("os.name");//包含Windows则说明是windows系统,返回trueif (property.contains("Windows")){return true;}return false;}
}
linux.driver=com.mysql.jdbc.Driver
linux.url=jdbc:mysql://localhost:3306/ssm
linux.username=root
linux.password=1234
-------------------------------------------------------------------------------------
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spring_day01
jdbc.username=root
jdbc.password=1234

4. 用于创建对象的注解

4.1 @Component和三个衍生注解

4.1.1 源码

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Indexed
public @interface Component {/*** The value may indicate a suggestion for a logical component name,* to be turned into a Spring bean in case of an autodetected component.* @return the suggested component name, if any (or empty String otherwise)*/String value() default "";
}@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Controller {/*** The value may indicate a suggestion for a logical component name,* to be turned into a Spring bean in case of an autodetected component.* @return the suggested component name, if any (or empty String otherwise)*/@AliasFor(annotation = Component.class)String value() default "";}@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Service {/*** The value may indicate a suggestion for a logical component name,* to be turned into a Spring bean in case of an autodetected component.* @return the suggested component name, if any (or empty String otherwise)*/@AliasFor(annotation = Component.class)String value() default "";}@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Repository {/*** The value may indicate a suggestion for a logical component name,* to be turned into a Spring bean in case of an autodetected component.* @return the suggested component name, if any (or empty String otherwise)*/@AliasFor(annotation = Component.class)String value() default "";}

4.1.2 说明

  • 作用:

    这四个注解都是用于修饰类的,为当前类创建一个对象存入Spring的IOC容器中。在实例化时默认无参构造函数,同时支持带参构造函数,前提是构造函数的参数依赖必须要有值,否则抛异常。

  • 属性:

    value : 用于指定存入容器时bean的id。当不指定时,默认值为当前类的名称。

  • 使用场景:

    当我们需要把自己编写的类注入到IOC容器中,可以使用以上四个注解实现。以上四个注解中@Component注解通常用在非三层对象中,而@Controller,@Service,@Repository三个注解一般是针对三层对象使用的,提供更加精确的语义化配置。

    需要注意的是,Spring在注解驱动开发时,要求必须先接管类对象,然后会处理类中的属性和方法。如果类没有被Spring接管,那么里面的属性和方法上的注解都不会被解析。

4.1.3 示例

  • 正确的方式1:使用默认构造函数。
/*** 用于记录系统日志* @author hao*/@Componentpublic class LogUtil {/*** 默认无参构造函数*/public LogUtil(){}//可以使用aop思想实现系统日志的记录}
  • 正确的方式2:在构造函数中注入一个已经在容器中的bean对象。
/*** 此处只是举例:使用JdbcTemplate作为持久层中的操作数据库对象* @author hao*/@Repository("userDao")public class UserDaoImpl implements UserDao{private JdbcTemplate jdbcTemplate ;/*** 此时要求容器中必须有JdbcTemplate对象* @param jdbcTemplate*/public UserDaoImpl(JdbcTemplate jdbcTemplate){this.jdbcTemplate = jdbcTemplate;}}
  • 正确的方式3:在构造函数中注入一个在配置文件获取到的值。
/*** 用于记录系统日志* @author hao*/@Componentpublic class LogUtil {/*** 构造时,注入日志级别* @param logLevel*/public LogUtil(@Value("${log.level}")String logLevel){System.out.println(logLevel);}//可以使用aop思想实现系统日志的记录}
  • 错误的方式:由于logLevel没有值,所以运行会报错。
/*** 用于记录系统日志* @author hao*/@Componentpublic class LogUtil {/*** 构造时,注入日志级别* @param logLevel*/public LogUtil(String logLevel){System.out.println(logLevel);}//可以使用aop思想实现系统日志的记录}

5. 用于注入数据的注解

5.1 @Autowired

5.1.1 源码

@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {/*** Declares whether the annotated dependency is required.* <p>Defaults to {@code true}.*/boolean required() default true;}

5.1.2 说明

  • 作用:

    自动按照类型注入。当IOC容器中有且只有一个类型匹配时可以直接注入成功。当有超过一个匹配时,则使用变量名称(写在方法上就是方法名称)作为bean的id,在符合类型的bean中再次匹配,能匹配上就可以注入成功。当匹配不上时,是否报错要看required属性的取值。

  • 属性:

    required:是否必须注入成功。默认值是true,表示必须注入成功。当取值为true的时候,注入不成功会报错。

  • 使用场景:

    此注解的使用场景非常之多,在实际开发中应用广泛。通常情况下我们自己写的类中注入依赖bean对象时,都可以采用此注解。

5.1.3 示例

/*** 此处只是举例:使用Jdbc作为持久层中的操作数据库对象* @author hao*/
@Repository("accountDao")
public class AccountDaoImpl implements AccountDao{@Autowiredprivate JdbcTemplate jdbcTemplate ;
}

5.2 @Qualifier

5.2.1 源码

@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Qualifier {String value() default "";}

5.2.2 说明

  • 作用:

    当使用自动按类型注入时,遇到有多个类型匹配的时候,就可以使用此注解来明确注入哪个bean对象。注意它通常情况下都必须与@Autowired注解一起使用

  • 属性:

    value:用于指定bean的唯一标识。

  • 使用场景:

    在项目开发中,很多时候都会用到消息队列,以ActiveMQ为例。当和spring整合之后,Spring框架提供了一个JmsTemplate对象,它既可以用于发送点对点模型消息也可以发送主题模型消息。如果项目中两种消息模型都用上了,那么针对不同的代码,将会注入不同的JmsTemplate,而容器中出现两个之后,就可以使用此注解注入。当然不用也可以,我们只需要把要注入的变量名称改为和要注入的bean的id一致即可。

5.3 @Resource

5.3.1 源码

@Target({TYPE, FIELD, METHOD})
@Retention(RUNTIME)
public @interface Resource {String name() default "";String lookup() default "";Class<?> type() default java.lang.Object.class;enum AuthenticationType {CONTAINER,APPLICATION}AuthenticationType authenticationType() default AuthenticationType.CONTAINER;boolean shareable() default true;String mappedName() default "";String description() default "";
}

5.3.2 说明

  • 作用:

    作用是找到依赖组件注入到应用来,它利用了JNDI(Java Naming and Directory Interface Java命名目录接口 J2EE规范之一)技术查找所需的资源。如果所有属性都不指定,它默认按照byType的方式装配bean对象。如果指定了name,没有指定type,则采用byName。如果没有指定name,而是指定了type,则按照byType装配bean对象。当byName和byType都指定了,两个都会校验,有任何一个不符合条件就会报错。

  • 属性:

    name:资源的JNDI名称。在spring的注入时,指定bean的唯一标识。
    type:指定bean的类型。
    lookup: 引用指向的资源名称。它可以使用全局JNDI名称链接到任何兼容的资源。
    authenticationType: 指定资源的身份验证类型。它只能为任何受支持类型的连接工厂的资源指定此选项,而不能为其他类型的资源指定此选项。
    shareable:指定此资源是否可以在此组件和其他组件之间共享。
    mappedName:指定资源的映射名称。
    description:指定资源的描述。

  • 使用场景:

    当我们某个类的依赖bean在ioc容器中存在多个的时候,可以使用此注解指定特定的bean对象注入。当然我们也可以使用@Autowired配合@Qualifier注入。

5.4 @Value

5.4.1 源码

@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Value {/*** The actual value expression: for example {@code #{systemProperties.myProp}}.*/String value();}

5.4.2 说明

  • 作用:

    用于注入基本类型和String类型的数据。它支持spring的EL表达式,可以通过${} 的方式获取配置文件中的数据。配置文件支持properties,xml和yml文件。

  • 属性:

    value : 指定注入的数据或者spring的el表达式。

  • 使用场景:

    在实际开发中,像连接数据库的配置,发送邮件的配置等等,都可以使用配置文件配置起来。此时读取配置文件就可以借助spring的el表达式读取。

5.4.3 示例

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spring_day01
jdbc.username=root
jdbc.password=1234
/*** 连接数据库的配置* @author hao*/
public class JdbcConfig {@Value("${jdbc.driver}")private String driver;@Value("${jdbc.url}")private String url;@Value("${jdbc.username}")private String username;@Value("${jdbc.password}")private String password;/*** windows系统注入的数据源* @return*/@Bean("dataSource")public DataSource createWindowsDataSource(){DriverManagerDataSource dataSource = new DriverManagerDataSource();dataSource.setDriverClassName(driver);dataSource.setUrl(url);dataSource.setUsername(username);dataSource.setPassword(password);return dataSource;}}

5.5 @Inject

5.5.1 源码

@Target({ METHOD, CONSTRUCTOR, FIELD })
@Retention(RUNTIME)
@Documented
public @interface Inject {
}

5.5.2 说明

  • 作用:

    用于建立依赖关系,和@Resource和@Autowired的作用是一样的。

在使用之前需要先导入坐标:<!-- https://mvnrepository.com/artifact/javax.inject/javax.inject --><dependency><groupId>javax.inject</groupId><artifactId>javax.inject</artifactId><version>1</version></dependency>
但是他们之前也有区别:@Autowired:来源于spring框架自身。默认是byType自动装配,当配合了@Qualifier注解之后,由@Qualifier实现byName装配。它有一个required属性,用于指定是否必须注入成功。@Resource:来源于JSR-250规范。在没有指定name属性时是byType自动装配,当指定了name属性之后,采用byName方式自动装配。@Inject:来源于JSR-330规范。它不支持任何属性,但是可以配合@Qualifier或者@Primary注解使用。同时,它默认是采用byType装配,当指定了JSR-330规范中的@Named注解之后,变成byName装配。
  • 属性:无

  • 使用场景:

    在使用@Autowired注解的地方,都可以替换成@Inject。它也可以出现在方法上,构造函数上和字段上,但是需要注意的是:因为JRE无法决定构造方法注入的优先级,所以规范中规定类中只能有一个构造方法带@Inject注解。

5.5.3 示例

/** * 第一种写法: 写在字段上* 此处只是举例:使用Jdbc作为持久层中的操作数据库对象* @author hao*/
@Repository("userDao")
public class UserDaoImpl implements UserDao{@Injectprivate JdbcTemplate jdbcTemplate ;}/*** 第二种写法:写在构造函数上* 此处只是举例:使用Jdbc作为持久层中的操作数据库对象* @author hao*/
@Repository("accountDao")
public class AccountDaoImpl implements AccountDao{private JdbcTemplate jdbcTemplate ;/*** 此时要求容器中必须有JdbcTemplate对象* @param jdbcTemplate*/@Injectpublic AccountDaoImpl(JdbcTemplate jdbcTemplate){this.jdbcTemplate = jdbcTemplate;}
}/*** 第三种写法:配合@Named注解使用* 此处只是举例:使用Jdbc作为持久层中的操作数据库对象* @author hao*/
@Repository("accountDao")
public class AccountDaoImpl implements AccountDao{@Inject@Named("jdbcTemplate")private JdbcTemplate jdbcTemplate ;
}

5.6 @Primary

5.6.1 源码

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Primary {}

5.6.2 说明

  • 作用:

    用于指定bean的注入优先级,被@Primary修饰的bean对象优先注入。

  • 属性:无

  • 使用场景:

    当依赖对象存在多个时,@Autowired注解已经无法完成功能,此时我们首先想到的是@Qualifier注解指定依赖bean的id。但是此时就产生了,无论有多少个bean,每次都会使用指定的bean注入。但是当我们使用@Primary,表示优先使用被@Primary注解的bean,但是当不存在时还会使用其他的。

5.6.3 示例

/*** @author hao*/
@Service("accountService")
public class AccountServiceImpl implements AccountService {@Autowired
//    @Qualifier("accountImpl1")private AccountDao accountDao;public void save(){System.out.println(accountDao);}
}/*** 此处只是举例:使用Jdbc作为持久层中的操作数据库对象* @author hao*/
@Repository("accountDaoImpl1")
public class AccountDaoImpl implements AccountDao{@Overridepublic String toString() {return "accountDaoImpl1";}
}/*** 此处只是举例:使用Jdbc作为持久层中的操作数据库对象* @author hao*/
@Repository("accountDaoImpl2")
@Primary
public class AccountDaoImpl2 implements AccountDao{@Overridepublic String toString() {return "accountDaoImpl2";}
}

6. 和生命周期以及作用范围相关的注解

6.1 @Scope

6.1.1 源码

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Scope {/*** Alias for {@link #scopeName}.* @see #scopeName*/@AliasFor("scopeName")String value() default "";/*** Specifies the name of the scope to use for the annotated component/bean.* <p>Defaults to an empty string ({@code ""}) which implies* {@link ConfigurableBeanFactory#SCOPE_SINGLETON SCOPE_SINGLETON}.* @since 4.2* @see ConfigurableBeanFactory#SCOPE_PROTOTYPE* @see ConfigurableBeanFactory#SCOPE_SINGLETON* @see org.springframework.web.context.WebApplicationContext#SCOPE_REQUEST* @see org.springframework.web.context.WebApplicationContext#SCOPE_SESSION* @see #value*/@AliasFor("value")String scopeName() default "";/*** Specifies whether a component should be configured as a scoped proxy* and if so, whether the proxy should be interface-based or subclass-based.* <p>Defaults to {@link ScopedProxyMode#DEFAULT}, which typically indicates* that no scoped proxy should be created unless a different default* has been configured at the component-scan instruction level.* <p>Analogous to {@code <aop:scoped-proxy/>} support in Spring XML.* @see ScopedProxyMode*/ScopedProxyMode proxyMode() default ScopedProxyMode.DEFAULT;}

6.1.2 说明

  • 作用:用于指定bean对象的作用范围。

  • 属性:

    value: 指定作用范围的取值。在注解中默认值是""。但是在spring初始化容器时,会借助ConfigurableBeanFactory接口中的类成员:String SCOPE_SINGLETON = "singleton";
    scopeName: 和value的作用是一样的。
    proxyMode: 指定bean对象的代理方式。指定ScopedProxyMode枚举的值 :
    DEFAULT:默认值。(就是NO)
    NO:不使用代理。
    INTERFACES:使用JDK官方的基于接口的代理。
    TARGET_CLASS:使用CGLIB基于目标类的子类创建代理对象。

  • 使用场景:

    在实际开发中,我们的bean对象默认都是单例的。通常情况下,被spring管理的bean都使用单例模式来创建。但是也有例外,例如Struts2框架中的Action,由于有模型驱动和OGNL表达式的原因,就必须配置成多例的。

6.2 @PostConstruct

6.2.1 源码

@Documented
@Retention (RUNTIME)
@Target(METHOD)
public @interface PostConstruct {
}

6.2.2 说明

  • 作用:用于指定bean对象的初始化方法。

  • 属性:无

  • 使用场景:在bean对象创建完成后,需要对bean中的成员进行一些初始化的操作时,就可以使用此注解配置一个初始化方法,完成一些初始化的操作。

6.3 @PreDestroy

6.3.1 源码

@Documented
@Retention (RUNTIME)
@Target(METHOD)
public @interface PreDestroy {
}

6.3.2 说明

  • 作用:用于指定bean对象的销毁方法。

  • 属性:无

  • 使用场景:在bean对象销毁之前,可以进行一些清理操作。

7. @Import注解的使用

7.1 ImportSelector和ImportBeanDefinitionRegistrar介绍

我们在注入bean对象时,可选的方式有很多种。例如:

  • 我们自己写的类,可以使用@Component,@Service,@Repository,@Controller等等。

  • 我们导入第三方库中的类时可以使用@Bean(当需要做一些初始化操作时,比如DataSource),也可以使用@Import注解,直接指定要引入的类的字节码。

但当我们的类很多时,在每个类上加注解会很繁琐,同时使用@Bean或者@Import写起来也很麻烦。此时我们就可以采用自定义ImportSelector或者ImportBeanDefinitionRegistrar来实现。

共同点:他们都是用于动态注册bean对象到容器中的。并且支持大批量的bean导入。

区别:

  • ImportSelector是一个接口,我们在使用时需要自己提供实现类。实现类中返回要注册的bean的全限定类名数组,然后执行ConfigurationClassParser类中的processImports方法注册bean对象。

  • ImportBeanDefinitionRegistrar也是一个接口,需要我们自己编写实现类,在实现类中手动注册bean到容器中。

实现ImportSelector接口或者ImportBeanDefinitionRegistrar接口的类不会被解析成一个Bean注册到容器中。同时,在注册到容器中时bean的唯一标识是全限定类名,而非短类名。

7.2 自定义ImportSelector

/*** @author hao*/
public interface UserService {void saveUser();
}/*** @author hao*/
public class UserServiceImpl implements UserService {@Overridepublic void saveUser() {System.out.println("保存用户");}
}/*** @author hao*/
@Configuration
@ComponentScan("com.test")
@Import(CustomeImportSelector.class)
public class SpringConfiguration {
}
/*** customeimport.properties配置文件中的内容:* 		custome.importselector.expression= com.test.service.impl.** @author hao*/
public class CustomeImportSelector implements ImportSelector {private String expression;public CustomeImportSelector(){try {Properties loadAllProperties = PropertiesLoaderUtils.loadAllProperties("customeimport.properties");expression = loadAllProperties.getProperty("custome.importselector.expression");} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}/*** 生成要导入的bean全限定类名数组* @param importingClassMetadata* @return*/@Overridepublic String[] selectImports(AnnotationMetadata importingClassMetadata) {//1.定义扫描包的名称String[] basePackages = null;//2.判断有@Import注解的类上是否有@ComponentScan注解if (importingClassMetadata.hasAnnotation(ComponentScan.class.getName())) {//3.取出@ComponentScan注解的属性Map<String, Object> annotationAttributes = importingClassMetadata.getAnnotationAttributes(ComponentScan.class.getName());//4.取出属性名称为basePackages属性的值basePackages = (String[]) annotationAttributes.get("basePackages");}//5.判断是否有此属性(如果没有ComponentScan注解则属性值为null,如果有ComponentScan注解,则basePackages默认为空数组)if (basePackages == null || basePackages.length == 0) {String basePackage = null;try {//6.取出包含@Import注解类的包名basePackage = Class.forName(importingClassMetadata.getClassName()).getPackage().getName();} catch (ClassNotFoundException e) {e.printStackTrace();}//7.存入数组中basePackages = new String[] {basePackage};}//8.创建类路径扫描器ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);//9.创建类型过滤器(此处使用切入点表达式类型过滤器)TypeFilter typeFilter = new AspectJTypeFilter(expression,this.getClass().getClassLoader());//10.给扫描器加入类型过滤器scanner.addIncludeFilter(typeFilter);//11.创建存放全限定类名的集合Set<String> classes = new HashSet<>();//12.填充集合数据for (String basePackage : basePackages) {scanner.findCandidateComponents(basePackage).forEach(beanDefinition -> classes.add(beanDefinition.getBeanClassName()));}//13.按照规则返回return classes.toArray(new String[classes.size()]);}
}
/*** 测试类* @author hao*/
public class SpringCustomeImportSelectorTest {public static void main(String[] args) {AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext("config");String[] names = ac.getBeanDefinitionNames();for(String beanName : names){Object obj = ac.getBean(beanName);System.out.println(beanName+"============"+obj);}}
}

7.3 自定义ImportBeanDefinitionRegistrar

借助7.2小节的案例代码,只需要把配置改一下:

/*** @author hao*/
@Configuration
@ComponentScan("com.test")
@Import(CustomeImportDefinitionRegistrar.class)
public class SpringConfiguration {
}/*** 自定义bean导入注册器* @author hao*/
public class CustomeImportDefinitionRegistrar implements ImportBeanDefinitionRegistrar {private String expression;public CustomeImportDefinitionRegistrar(){try {Properties loadAllProperties = PropertiesLoaderUtils.loadAllProperties("customeimport.properties");expression = loadAllProperties.getProperty("custome.importselector.expression");} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}@Overridepublic void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {//1.定义扫描包的名称String[] basePackages = null;//2.判断有@Import注解的类上是否有@ComponentScan注解if (importingClassMetadata.hasAnnotation(ComponentScan.class.getName())) {//3.取出@ComponentScan注解的属性Map<String, Object> annotationAttributes = importingClassMetadata.getAnnotationAttributes(ComponentScan.class.getName());//4.取出属性名称为basePackages属性的值basePackages = (String[]) annotationAttributes.get("basePackages");}//5.判断是否有此属性(如果没有ComponentScan注解则属性值为null,如果有ComponentScan注解,则basePackages默认为空数组)if (basePackages == null || basePackages.length == 0) {String basePackage = null;try {//6.取出包含@Import注解类的包名basePackage = Class.forName(importingClassMetadata.getClassName()).getPackage().getName();} catch (ClassNotFoundException e) {e.printStackTrace();}//7.存入数组中basePackages = new String[] {basePackage};}//8.创建类路径扫描器ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(registry, false);//9.创建类型过滤器(此处使用切入点表达式类型过滤器)TypeFilter typeFilter = new AspectJTypeFilter(expression,this.getClass().getClassLoader());//10.给扫描器加入类型过滤器scanner.addIncludeFilter(typeFilter);//11.扫描指定包scanner.scan(basePackages);}
}

8. @Profile注解的使用

8.1 使用场景分析

@Profile注解是spring提供的一个用来标明当前运行环境的注解。我们正常开发过程中经常遇到的问题是,开发环境是一套环境,测试是一套环境,线上部署又是一套环境。这样从开发到测试再到部署,会对程序中的配置修改多次,尤其是从测试到上线这个环节,测试人员也不敢保证改了哪个配置之后还能不能在线上运行。为了解决上面的问题,我们一般会使用一种方法,就是针对不同的环境进行不同的配置,从而在不同的场景中跑我们的程序。而spring中的@Profile注解的作用就体现在这里。在spring使用DI来注入的时候,能够根据当前制定的运行环境来注入相应的bean。最常见的就是使用不同的DataSource了。

8.2 代码实现

  • 自定义不同环境的数据源
package config;import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Profile;/*** @author hao*/
public class JdbcConfig {@Value("${jdbc.driver}")private String driver;@Value("${jdbc.url}")private String url;@Value("${jdbc.username}")private String username;@Value("${jdbc.password}")private String password;/*** @return*/@Bean("dataSource")@Profile("dev")public DruidDataSource createDevDataSource(){DruidDataSource dataSource = new DruidDataSource();dataSource.setDriverClassName(driver);dataSource.setUrl(url);dataSource.setUsername(username);dataSource.setPassword(password);dataSource.setMaxActive(10);return dataSource;}/*** @return*/@Bean("dataSource")@Profile("test")public DruidDataSource createTestDataSource(){DruidDataSource dataSource = new DruidDataSource();dataSource.setDriverClassName(driver);dataSource.setUrl(url);dataSource.setUsername(username);dataSource.setPassword(password);dataSource.setMaxActive(50);return dataSource;}/*** @return*/@Bean("dataSource")@Profile("produce")public DruidDataSource createProduceDataSource(){DruidDataSource dataSource = new DruidDataSource();dataSource.setDriverClassName(driver);dataSource.setUrl(url);dataSource.setUsername(username);dataSource.setPassword(password);dataSource.setMaxActive(100);return dataSource;}
}
  • 编写配置类
/*** @author hao*/
@Configuration
@Import(JdbcConfig.class)
public class SpringConfiguration {
}
  • 编写测试类
/*** @author hao*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfiguration.class)
@ActiveProfiles("test")
public class SpringProfileTest {@Autowiredprivate DruidDataSource druidDataSource;@Testpublic void testProfile(){System.out.println(druidDataSource.getMaxActive());}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/828550.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

虚拟机VMware安装与Ubuntu

1.虚拟机安装 链接&#xff1a;百度网盘 请输入提取码 提取码&#xff1a;2fr6 CG54H-D8D0H-H8DHY-C6X7X-N2KG6 2.Ubuntu下载 Download Ubuntu Desktop | Ubuntu 3.设置 如后续要下一些软件越大越好

Chrome 侧边栏开发示例

前言 最近做项目&#xff0c;需要开发浏览器扩展&#xff0c;但是考虑页面布局兼容性问题&#xff0c;使用了Chrome114开始的侧边栏&#xff0c;浏览器自带的能力毕竟不会出现兼容性问题&#xff0c;不过Chrome123开始&#xff0c;侧边栏居然又可以选择固定右侧扩展栏了&#…

【黑马头条】-day11热点文章实时计算-kafka-kafkaStream-Redis

文章目录 今日内容1 实时流式计算1.1 应用场景1.2 技术方案选型 2 Kafka Stream2.1 概述2.2 KafkaStream2.3 入门demo2.3.1 需求分析2.3.2 实现2.3.2.1 添加依赖2.3.2.2 创建快速启动&#xff0c;生成kafka流2.3.2.3 修改生产者2.3.2.4 修改消费者2.3.2.5 测试 2.4 SpringBoot集…

光伏无人机巡检主要有些什么功能和特点?

随着科技的飞速发展&#xff0c;无人机技术已经渗透到多个行业领域&#xff0c;光伏产业便是其中之一。光伏无人机巡检&#xff0c;作为一种新兴的巡检方式&#xff0c;正在逐渐取代传统的人工巡检&#xff0c;为光伏电站的安全、高效运行提供了有力保障。那么&#xff0c;光伏…

记录ubuntu20.04安装nvidia-525.85.05显卡驱动(学习笔记2024.4.15、4.16)

电脑&#xff1a;华硕天选X2024 显卡&#xff1a;4060Ti i5-14400F 架构&#xff1a;x86_64 我需要使用Linux系统使用IsaacSim进行仿真&#xff0c;所以安装的都是IsaacSim中的推荐版本。 一.对新鲜的电脑进行分盘 电脑刚到手&#xff0c;900多个G全在C盘里&#xff0c;给它…

【C语言__联合和枚举__复习篇10】

目录 前言 一、联合体 1.1 联合体的概念 1.2 联合体与结构体关于声明和内存布局的比较 1.3 联合体的大小如何计算 1.4 使用联合体的2个示例 二、枚举体 2.2 枚举体的概念 2.2 枚举体的优点 前言 本篇主要讨论以下问题&#xff1a; 1. 联合体是什么&#xff0c;它有什么特点 …

如何通过ABAP将数据写回BPC模型

今天给大家安利一个标准方法&#xff0c;主要用于回写BPC模型数据&#xff0c;一般情况下&#xff0c;BPC模型数据的回写&#xff0c;标准的方式有数据抽取&#xff0c;直接抽取供数模型的数&#xff0c;利用BW标准转换方式进行供数&#xff0c;又或者通过逻辑脚本BADI的方式直…

Python之PCV库安装教程以及解说

PCV库是一个比较古老的python库 在网上参考了很多教程 于是现在想要总结一下,并且分享整理了一下资源 很多人是通过pycharm内部直接下载PCV 但是导入时还要报错 因为PCV版本不对 pycharm自动下载的版本过于旧 是0.0.2 而我们需要的是1.0.0版本 否则下面PCV部分会报错无法导入…

LeetCode - LCR 179.查找总价格为目标值的两个商品

一. 题目链接 LeetCode - LCR 179. 查找总价格为目标值的两个商品 解法&#xff08;双指针 - 对撞指针&#xff09;&#xff1a; 算法思路&#xff1a; 注意到本题是升序的数组&#xff0c;因此可以用「对撞指针」优化时间复杂度。 算法流程&#xff1a; 初始化left &#…

如何快速申请SSL证书实现HTTPS访问?

申请SSL证书最简单的方法通常涉及以下几个步骤&#xff0c;尽量简化了操作流程和所需专业知识&#xff1a; 步骤一&#xff1a;选择适合的SSL证书类型 根据您的网站需求&#xff0c;选择最基础的域名验证型&#xff08;DV SSL&#xff09;证书&#xff0c;它通常只需验证域名所…

【以奖代补】诗情画意润童心 书香课堂志愿行

中华古诗词历史源远流长&#xff0c;名篇佳作数不胜数。为弘扬民族文化精髓&#xff0c;丰富乡村儿童假期生活。2024年4月21日上午&#xff0c;襄州区社会工作者协会联合襄州区张家集镇社工站、张集村“童叟乐园”志愿服务队在张集村开展“诗情画意润童心 书香课堂志愿行”志愿…

JavaSE:抽象

一&#xff0c;抽象是什么&#xff0c;抽象和面向对象有什么关系 抽象&#xff0c;个人理解&#xff0c;就是抽象的意思 我们都知道面向对象的四大特征&#xff1a;封装&#xff0c;继承&#xff0c;多态&#xff0c;抽象 为什么抽象是面向对象的特征之一&#xff0c;抽象和面…

【ESP32入门实战】初识ESP32

【ESP32入门实战】初识ESP32 文章目录 【ESP32入门实战】初识ESP32&#x1f468;‍&#x1f3eb;前言【写作缘由】&#x1f9d1;‍&#x1f393;ESP32介绍&#x1f469;‍&#x1f4bb;ESP32-WROOM-32&#x1f469;‍&#x1f4bb;ESP32的组成部分 &#x1f468;‍&#x1f3eb…

记内网http洪水攻击,导致网页无法访问一事

事由 最近两日&#xff0c;部分同事在访问税纪云平台时&#xff0c;登录跳转页面频繁转圈、要么就是出现无法连接的错误提示。 无法访问此页面 已重置连接。 请尝试: 检查连接检查代理和防火墙运行 Windows 网络诊断经过以下几方面的排查&#xff0c;无果。 后续通过检查…

4月23号总结

java实现发送邮件 在做聊天室项目的时候&#xff0c;由于需要发送邮箱验证码&#xff0c;所以自己查找了这方面的内容。 首先需要在Maven里面依赖 <dependency><groupId>com.sun.mail</groupId><artifactId>javax.mail</artifactId><versio…

冯喜运:4.25黄金原油上演过山车走势附操作建议

【黄金消息面分析】&#xff1a;周四&#xff08;4月25日&#xff09;亚洲时段&#xff0c;现货黄金窄幅震荡&#xff0c;目前交投于2320美元/盎司附近。金价周三企稳在2300关口上方&#xff0c;收报2315.80美元/盎司&#xff0c;中东紧张局势导致的风险溢价有所缓和&#xff0…

数据类型与变量(Java)

数据类型与变量&#xff08;Java&#xff09; 字面常量数据类型变量变量概念整型变量整型变量长整型变量短整型变量字节型变量 浮点型变量双精度浮点型单精度浮点型 字符型变量布尔型变量类型转换自动类型转换(隐式)强制类型转换(显式) 类型提升 字面常量 常量&#xff1a;常量…

AI预测体彩排列3第2套算法实战化测试第3弹2024年4月25日第3次测试

今天继续进行新算法的测试&#xff0c;今天是第3次测试。好了&#xff0c;废话不多说了&#xff0c;直接上图上结果。 2024年4月25日体彩排3预测结果 6码定位方案如下&#xff1a; 百位&#xff1a;4、5、3、6、1、0 十位&#xff1a;6、5、4、3、1、0 个位&#xff1a;6、2、7…

24二战上岸北邮计算机经验贴(初试+复试超详细)

个人情况介绍 我本科就读于南方的一所211&#xff0c;学的专业是网络工程。学习成绩在班级里是中等水平&#xff0c;挂了两门课。之前专业课学得不仔细&#xff0c;因此考研备考相当于在学新知识。我是二战上岸的&#xff0c;两次都是报考的是北邮计算机科学与技术&#xff08…

ASP.NET基于WEB的选课系统

摘要 设计本系统的目的是对选课信息进行管理。学生选课系统维护模块主要完成的是系统管理与维护功能。课题研究过程中&#xff0c;首先对系统管理模块进行了详尽的需求分析&#xff0c;经分析得到系统管理模块主要完成如下的功能&#xff1a;用户基本信息、选课信息的录入,查看…