Java 五种方法实现普通类注入spring管理的service、repository等资源

场景

在一些普通的工具类或者POJO类需要注入service或者repository等spring管理的资源进行一定的逻辑处理,普通类不受spring管理,所以利用@Autowired实现不了。本文介绍几种方式解决此问题。

本文利用老师与学生的关系作为例子讲解,一个老师可以管理多个学生,在查询老师列表时也将他名下的学生查询出来,Teacher表与Student不进行关联表处理,那么就需要在Teacher的Entity里注入StudentService进行查询。

数据准备

数据库

Student

 uuid | deleted |  status  | teacher 
------+---------+----------+---------
 s1   |         | active   | t1
 s2   | deleted | inactive | t1
(2 rows)

Teacher

 uuid 
------
 t1
 t2
(2 rows)

Entity

Teacher
不同的实现方法有不同的注入,具体的参考下文
Student

@Data
@Entity
@Table(name = "student")
@EqualsAndHashCode(of = {"id"})
public class Student {

	@Column(name = "uuid")
	@Id
	private String id;
	
	@Column(name = "deleted")
	private String deleted;
	
	@Column(name = "status")
	private String status;
	
	@Column(name = "teacher")
	private String teacher;
}

Repository

StudentReposity

@Repository
public interface StudentReposity extends CrudRepository<Student, String>{
	
	@Query("select s from Student s where s.teacher=:teacherId")
	public Set<Student> findStudentsByTeacher(@Param("teacherId") String teacherId);
}

TeacherRepository

@Repository
public interface TeacherRepository extends CrudRepository<Teacher, String>{
	@Query("select t from Teacher t")
	public Set<Teacher> findTeachers();
}

Service

StudentService

@Service
public class StudentService {

	@Autowired
	StudentReposity studentReposity;
	
	public Set<Student> findStudentsByTeacher(String teacherId){
		return studentReposity.findStudentsByTeacher(teacherId);
	}
}

TeacherService

@Service
public class TeacherService {
	@Autowired
	TeacherRepository teacherRepository;
	
	public Set<Teacher> findTeachers(){
		return teacherRepository.findTeachers();
	}
}

Controller

@RestController
public class TeacherController {

	@Autowired
	TeacherService teacherService;
	
	@GetMapping("/teachers")
	public Set<Teacher> findTeachers(){
		return teacherService.findTeachers();
	}
}

解决方法

一、利用@PostConstruct

这种方法比较简单,但是如果你有多个Entity需要注入多个service的时候就比较麻烦一点,另外sonarqube代码检测也会有警告。
Entity

@Data
@Entity
@Table(name = "teacher")
@EqualsAndHashCode(of = {"id"})
public class Teacher {

	@Column(name = "uuid")
	@Id
	private String id;
	
	@Transient
	private Set<Student> students;
	
	public Set<Student> getStudents(){
		return SpringBeanUtil.staticStudentService.findStudentsByTeacher(getId());
	}
}

利用@PostConstruct

@Component
public class SpringBeanUtil {
	@Autowired
	StudentService studentService;
	
	public static StudentService staticStudentService;
	
	@PostConstruct
	public void init() {
		staticStudentService = studentService;
	}
}

二、利用ENUM枚举类

Entity

@Data
@Entity
@Table(name = "teacher")
@EqualsAndHashCode(of = {"id"})
public class Teacher {

	@Column(name = "uuid")
	@Id
	private String id;

	@Transient
	private Set<Student> students;
	
	public Set<Student> getStudents(){
		return EnumBeanUtil.BEAN.getBean(StudentService.class).findStudentsByTeacher(getId());
	}
}

定义获取ApplicatinContext的Enum

public enum EnumBeanUtil {

	BEAN;
	
	private ApplicationContext applicationContext;

	public ApplicationContext getApplicationContext() {
		return applicationContext;
	}

	public void setApplicationContext(ApplicationContext applicationContext) {
		this.applicationContext = applicationContext;
	}
	
	public <T> T getBean(Class<T> clazz) {
		return getApplicationContext().getBean(clazz);
	}
}

设置ENUM的applicationContext变量

@SpringBootApplication
public class Application extends SpringBootServletInitializer  {
	
    public static void main(String[] args) {
        EnumBeanUtil.BEAN.setApplicationContext(SpringApplication.run(Application.class, args));
    }

}

启动测试,可以得到studnets。

三、利用ApplicationContextAware

Entity

@Data
@Entity
@Table(name = "teacher")
@EqualsAndHashCode(of = {"id"})
public class Teacher {

	@Column(name = "uuid")
	@Id
	private String id;
	
	@Transient
	private Set<Student> students;
	
	public Set<Student> getStudents(){
		StudentService studentService = BeanUtil.getBean(StudentService.class);
		return studentService.findStudentsByTeacher(getId());
	}
}

1、实现ApplicationContextAware接口

@Component
public class BeanUtil implements ApplicationContextAware{

    //@note
	private static ApplicationContext context;
	
	@Override
	public void setApplicationContext(ApplicationContext applicationContext) {
		BeanUtil.context = applicationContext;
	}
	
	public static <T> T getBean(Class<T> beanClass) {
        return context.getBean(beanClass);
    }

}

@note: 如果你的代码有sonarqube检查的话会有critical的警告,所以如果你有严格的代码规范的话这种方法不推荐。

测试

发起/teachers请求可得到结果:

[
    {
        "id": "t1",
        "students": [
            {
                "id": "s1",
                "deleted": null,
                "status": "active",
                "teacher": "t1"
            },
            {
                "id": "s2",
                "deleted": "deleted",
                "status": "inactive",
                "teacher": "t1"
            }
        ]
    },
    {
        "id": "t2",
        "students": []
    }
]

可以看到students查出来,证明StudnetService确实被注入进去了。

四、利用Hibernate Interceptor

Entity

@Data
@Entity
@Table(name = "teacher")
@EqualsAndHashCode(of = {"id"})
public class Teacher {

	@Column(name = "uuid")
	@Id
	private String id;

	@Transient
	private Set<Student> students;
	
	@Transient
	@JsonIgnore
	private StudentService studentService;
	
	public Set<Student> getStudents(){
		return studentService.findStudentsByTeacher(getId());
	}
}

1、创建自己的Interceptor并扩展EmptyInterceptor

public class StudentInterceptor extends EmptyInterceptor{

	/**
	 * 
	 */
	private static final long serialVersionUID = -4771865788342461458L;

	@Autowired
	@Lazy //@note1
	StudentService studentService;
	
	//@note2
	@Override
	public boolean onLoad(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) {
	    //@note3
		if(entity instanceof Teacher) {
			((Teacher) entity).setStudentService(studentService);
			return true;
		}
		return false;
	}
}

@note1: 懒加载防止启动的时候出现bean加载死循环
@note2:重写onLoad方法,在对象加载的时候就注入service。
@note3: 对象是Teacher的注入studentService。如果对象多可以在这里添加自己的需求。

2、实例化自定义的Interceptor
如果是注解方式配置的话:

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(entityManagerFactoryRef = "entityManagerFactory"
						, basePackages = { "***.your.repository" })
public class PersistenceConfiguration {

	@Primary
	@Bean(name = "entityManagerFactory")
	public LocalContainerEntityManagerFactoryBean entityManagerFactory(EntityManagerFactoryBuilder builder,
			@Qualifier("dataSource") DataSource dataSource) {
		//@note
		Map<String, Object> jpaProperties = new HashMap<>();
		jpaProperties.put("hibernate.ejb.interceptor", studentInterceptor());
		return builder.properties(jpaProperties)
		//your other configuration
		.build();
	}
	
	@Bean
	public StudentInterceptor studentInterceptor() {
		return new StudentInterceptor();
	}
    
    //other configuration
}

如果你的Spring boot版本号小于2,可以通过extends HibernateJpaAutoConfiguration 来实现,具体做法参考link.其他做法也可以参考该文章,但是我本人测都实现不了。

测试

发起/teachers请求可得到结果:

[
    {
        "id": "t1",
        "students": [
            {
                "id": "s1",
                "deleted": null,
                "status": "active",
                "teacher": "t1"
            },
            {
                "id": "s2",
                "deleted": "deleted",
                "status": "inactive",
                "teacher": "t1"
            }
        ]
    },
    {
        "id": "t2",
        "students": []
    }
]

可以看到students查出来,证明StudnetService确实被注入进去了。

五、利用AOP Injection

Entity
声明注解@Configurable、@Aspect。

@Data
@Entity
@Table(name = "teacher")
@EqualsAndHashCode(of = {"id"})
@Configurable
@Aspect
public class Teacher {

	@Column(name = "uuid")
	@Id
	private String id;
	
	@Transient
	private Set<Student> students;
	
	@Transient
	@JsonIgnore
	@Autowired
	private StudentService studentService;
	
	public Set<Student> getStudents(){
		return studentService.findStudentsByTeacher(getId());
	}
}

1、告诉spring激活aop injection

@SpringBootApplication
@EnableSpringConfigured  //@note。通知spring
public class Application extends SpringBootServletInitializer  {
	
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

}

2、使用AspectJ “Java Agent”
如果你是使用Eclipse,可以在Debug Configurations->Arguments->VM arguments添加==-javaagent:\your\path\to\aspectjweaver.jar==。需要下载aspectjweaver依赖。
如果你是直接打好包,则可执行java -jar your.jar -javaagent:\your\path\to\aspectjweaver.jar。

如果你是xml配置的话可以参考这篇文章.

要实现AOP可能还需要导入一些依赖:

<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-aop -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
    <version>2.2.6.RELEASE</version>
</dependency>

如果你还有其他方法,欢迎讨论*-*

参考:

https://blog.csdn.net/qq_39056805/article/details/80285915

https://blog.csdn.net/Mrs_chens/article/details/93741730

https://stackoverflow.com/questions/9666922/enable-spring-aop-or-aspectj

https://dzone.com/articles/inject-spring-beans-aspectj

https://stackoverflow.com/questions/27744287/what-is-the-spring-java-config-equivalent-of-contextspring-configured

https://stackoverflow.com/questions/10008714/requested-bean-is-currently-in-creation-is-there-an-unresolvable-circular-refer

https://stackoverflow.com/questions/25283767/how-to-use-spring-managed-hibernate-interceptors-in-spring-boot/29919009

http://jblewitt.com/blog/?p=129

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java Spring中,可以通过使用注解和配置文件来实现将配置参数注入到@Value注解中。下面是一些步骤来完成这个过程: 1. 导入所需的依赖:确保在你的项目中已经添加了Spring的相关依赖,如spring-context和spring-boot-starter等。 2. 创建一个配置:创建一个用于读取配置文件的配置。可以使用`@Configuration`注解标记该。 3. 配置属性文件:在你的项目中,创建一个属性文件(如application.properties或application.yml),并在其中定义你想要注入的配置参数。 4. 使用@Value注解:在你的中,使用`@Value`注解来注入属性值。可以在构造函数、字段或Setter方法上使用该注解。 5. 使用@ConfigurationProperties注解(可选):如果你有一个较大的配置参数集合,可以使用`@ConfigurationProperties`注解来将多个属性注入到一个POJO(普通Java对象)中,然后使用这个POJO来访问属性。 下面是一个示例代码: 首先,创建一个配置来读取属性文件: ```java @Configuration @PropertySource("classpath:application.properties") public class AppConfig { } ``` 然后,在你的中使用@Value注解来注入属性值: ```java @Component public class MyComponent { @Value("${my.property}") private String myProperty; // ... } ``` 在上面的示例中,`${my.property}`是你在属性文件中定义的属性名。 如果你有多个属性需要注入,可以使用@ConfigurationProperties注解来将它们注入到一个POJO中: ```java @Component @ConfigurationProperties(prefix = "my") public class MyProperties { private String property1; private String property2; // getter and setter methods } ``` 然后,在你的注入这个POJO对象: ```java @Component public class MyComponent { private final MyProperties properties; public MyComponent(MyProperties properties) { this.properties = properties; } // ... } ``` 在这个示例中,`my.property1`和`my.property2`是你在属性文件中定义的属性名。 请注意,为了使这些注解生效,你需要确保你的Spring容器管理。这可以通过在上添加@Component、@Service或@Repository等注解来实现。 希望这些步骤能帮助你成功注入jar包中的配置参数值!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值