首页 > 解决方案 > Java将泛型类定义为静态方法的参数,以传递实体对象

问题描述

亲爱的 Stackoverflow 社区,

我是使用泛型的新手,并且在正确使用泛型方面存在问题。我想要做的是,静态方法可以将通用对象作为参数。我的想法是,将实体对象作为参数传递,然后返回一个 UserDetailsImpl 对象。所以我想让这个方法能够处理不同的实体类并且不编写样板代码。为此,我为它编写了一个简单的 Box 类。

盒子.java



    public class Box<T> {
    // T stands for "Type"
    private T t;
    public void set(T t) { this.t = t; }
    public T get() { return t; }
    
}

现在我尝试使用它在静态构建方法中的UserDetailsImpl.java类中将通用对象作为参数传递:

package com.yildiz.tradilianz.security.services;

import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import com.fasterxml.jackson.annotation.JsonIgnore;

public class UserDetailsImpl implements UserDetails {
    private static final long serialVersionUID = 1L;
    
    private Long id;
    
    private String username;
    
    private String email;
    
    @JsonIgnore
    private String password;
    
    private Collection<? extends GrantedAuthority> authorities;
    
    public UserDetailsImpl(Long id, String username, String email, String password,
            Collection<? extends GrantedAuthority> authorities) {
        this.id = id;
        this.username = username;
        this.email = email;
        this.password = password;
        this.authorities = authorities;
    }
    
    public static UserDetailsImpl build(Box<Object> user) {
        List<GrantedAuthority> authorities = user.getRoles().stream()
                .map(role -> new SimpleGrantedAuthority(role.getName().name()))
                .collect(Collectors.toList());

        return new UserDetailsImpl(
                user.getId(), 
                user.getUsername(), 
                user.getEmail(),
                user.getPassword(), 
                authorities);
    }
    

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return authorities;
    }
    
    public Long getId() {
        return id;
    }
    
    public String getEmail() {
        return email;
    }

    @Override
    public String getPassword() {
        return password;
    }

    @Override
    public String getUsername() {
        return username;
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }
    
    @Override
    public boolean equals(Object o) {
        if (this == o)
            return true;
        if (o == null || getClass() != o.getClass())
            return false;
        UserDetailsImpl user = (UserDetailsImpl) o;
        return Objects.equals(id, user.id);
    }

}

现在的问题是,传递的对象不知道我尝试访问的整个 get() 方法,例如 user.getId()、user.getRoles()、user.getPassword() 等。我想要通用的 Box 类包含任何对象但如何声明我需要从中访问的此方法?

否则它会以“方法 getRoles() 未定义类型 Box”结束

我想作为通用对象传递的是我的客户实体类,但如果它返回 null 而不是我想在 **UserDetailsS​​erviceImpl ** 中测试零售商实体类是否有效等等:

package com.yildiz.tradilianz.security.services;

import org.apache.commons.validator.routines.EmailValidator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.yildiz.tradilianz.auth.User;
import com.yildiz.tradilianz.auth.UserRepository;
import com.yildiz.tradilianz.customer.Customer;
import com.yildiz.tradilianz.customer.CustomerDTO;
import com.yildiz.tradilianz.customer.CustomerRepository;

@Service
public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    CustomerRepository customerRepository;
    
    @Autowired
    CustomerDTO customerDTO;

    @Override
    @Transactional
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        // Check for username or email passed through username parameter
        boolean valid = EmailValidator.getInstance().isValid(username);
        if (valid == false) {
            
            /*
             * Build with UserDetailsImpl but if user for Customer class is null I want to try another repository and do 
             * something like this:
             * Retailer user = retailerRepository.findByUsername(username); And if it is not null
             * it should pass user Object which class is Retailer and so on.
             * 
             */
            Customer user = customerRepository.findByUsername(username);

            return UserDetailsImpl.build(user);
            
        } else {
            Customer user = customerRepository.findByEmail(username):

            return UserDetailsImpl.build(user);
            
        }
    }

}

客户实体类

package com.yildiz.tradilianz.customer;

import java.sql.Timestamp;
import java.util.HashSet;
import java.util.Set;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;

import org.hibernate.annotations.CreationTimestamp;

import com.yildiz.tradilianz.auth.ERole;


@Entity
public class Customer {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    @NotBlank
    @Size(max = 20)
    private String username;
    @NotBlank
    @Size(max = 120)
    private String password;
    @Column(nullable = false)
    private String givenName;
    @Column(nullable = false)
    private String surname;
    private String birthday;
    private String streetAddress;
    private String city;
    private String postalCode;
    @Column(updatable = false, nullable = false)
    private String email;
    private String phoneNumber;
    @CreationTimestamp
    private Timestamp timestamp;
    private Double balance;
    private Integer bonuspoints;
    private String role;

    protected Customer() {

    }

    public Customer(String username,String password, String givenName, String surname, String birthday, String streetAddress, String city,
            String postalCode, String email, String phoneNumber, Double balance, Integer bonuspoints, String role) {
        this.username = username;
        this.password = password;
        this.givenName = givenName;
        this.surname = surname;
        this.birthday = birthday;
        this.streetAddress = streetAddress;
        this.city = city;
        this.postalCode = postalCode;
        this.email = email;
        this.phoneNumber = phoneNumber;
        this.balance = balance;
        this.bonuspoints = bonuspoints;
        this.role = role;
    }

    @Override
    public String toString() {
        return ("Benutzername: "+username+ "Passwort: "+password+ "Vorname: " + givenName + " Nachname: " + surname +
                " Geburtstag: " + birthday + " Straße: "+ streetAddress + " Stadt: " + city + " Postleitzahl: " +
                postalCode + " E-Mail-Adresse: " + email+ " Telefonnummer: " + phoneNumber + "Kontostand: " + balance +
                " Bonuspunkte: " + bonuspoints+" Rolle:"+role);
    }
    public String getUsername() {
        return username;
    }
    
    public String getPassword() {
        return password;
    }

    public Long getId() {
        return id;
    }

    public String getGivenName() {
        return givenName;
    }

    public String getSurname() {
        return surname;
    }

    public String getBirthday() {
        return birthday;
    }

    public String getStreetAddress() {
        return streetAddress;
    }

    public String getCity() {
        return city;
    }

    public String getPostalCode() {
        return postalCode;
    }

    public String getEmail() {
        return email;
    }

    public String getPhoneNumber() {
        return phoneNumber;
    }

    public Timestamp getTimestamp() {
        return timestamp;
    }

    public Integer getBonuspoints() {
        return bonuspoints;
    }

    public Double getBalance() {
        return balance;
    }
    
    public String getRole() {
        return role;
    }
    

    public void setUsername(String username) {
        this.username = username;
    }
    
    public void setPassword(String password) {
        this.password = password;
    }

    public void setGivenName(String givenName) {
        this.givenName = givenName;
    }

    public void setSurname(String surname) {
        this.surname = surname;
    }

    public void setBirthday(String birthday) {
        this.birthday = birthday;
    }

    public void setStreetAddress(String streetAddress) {
        this.streetAddress = streetAddress;
    }

    public void setCity(String city) {
        this.city = city;
    }

    public void setPostalCode(String postalCode) {
        this.postalCode = postalCode;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public void setPhoneNumber(String phoneNumber) {
        this.phoneNumber = phoneNumber;
    }

    public void setBalance(Double balance) {
        this.balance = balance;
    }

    public void setBonuspoints(Integer bonuspoints) {
        this.bonuspoints = bonuspoints;
    }
    
    public void setRole(String role) {
        this.role = role;
    }

}

**

你能给我提示我如何以正确的方式做到这一点吗?

标签: javaspringauthenticationgenericsgeneric-programming

解决方案


在您的 build() 方法中,您传递的是 Box,因此它只知道类型为 Object。

public static UserDetailsImpl build(Box<Object> user)

当您尝试访问它时,您正在尝试访问来自 Customer 类型的方法,它不会知道哪些方法与 Customer 对象关联。(用户参考只会知道 Box 类的方法)

所以,你需要做的是改变Box<Object>Box<Customer>然后使用访问客户的方法

user.get().getId() etc.

这里 user.get() 将返回底层类型对象,您将可以访问它的方法。

或者您还可以做的是,如果您希望泛型类型成为特定的实例类型,您可以将 Box 类实现更改为(将 T 类型扩展到该类的实例),例如您的情况下的 Customer。(您可以创建一个接口或抽象类,其中包含您尝试使用的方法)

public class Box< T extends Customer>

public class Box<T extends CustomerInterface>  etc.

推荐阅读